Browse Source

Merge branch 'AvaloniaUI:master' into master

pull/6491/head
Sergey Mikolaytis 5 years ago
committed by GitHub
parent
commit
ca4268cc1b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      src/Avalonia.Controls/ComboBox.cs
  2. 38
      src/Avalonia.Controls/Mixins/DisposableMixin.cs
  3. 41
      src/Avalonia.Controls/Primitives/Popup.cs
  4. 99
      src/Avalonia.Controls/SystemDialog.cs
  5. 11
      src/Avalonia.Input/Navigation/TabNavigation.cs
  6. 1
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  7. 3
      src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
  8. 1
      src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
  9. 2
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  10. 7
      src/Windows/Avalonia.Win32/PlatformConstants.cs
  11. 6
      src/Windows/Avalonia.Win32/SystemDialogImpl.cs
  12. 4
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  13. 2
      src/Windows/Avalonia.Win32/Win32Platform.cs
  14. 4
      src/Windows/Avalonia.Win32/WindowImpl.cs
  15. 1
      tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
  16. 3
      tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
  17. 27
      tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs
  18. 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

30
src/Avalonia.Controls/ComboBox.cs

@ -1,6 +1,8 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reactive.Disposables;
using Avalonia.Controls.Generators; using Avalonia.Controls.Generators;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
@ -80,7 +82,7 @@ namespace Avalonia.Controls
private bool _isDropDownOpen; private bool _isDropDownOpen;
private Popup _popup; private Popup _popup;
private object _selectionBoxItem; private object _selectionBoxItem;
private IDisposable _subscriptionsOnOpen; private readonly CompositeDisposable _subscriptionsOnOpen = new CompositeDisposable();
/// <summary> /// <summary>
/// Initializes static members of the <see cref="ComboBox"/> class. /// Initializes static members of the <see cref="ComboBox"/> class.
@ -291,6 +293,7 @@ namespace Avalonia.Controls
_popup = e.NameScope.Get<Popup>("PART_Popup"); _popup = e.NameScope.Get<Popup>("PART_Popup");
_popup.Opened += PopupOpened; _popup.Opened += PopupOpened;
_popup.Closed += PopupClosed;
} }
internal void ItemFocused(ComboBoxItem dropDownItem) internal void ItemFocused(ComboBoxItem dropDownItem)
@ -303,8 +306,7 @@ namespace Avalonia.Controls
private void PopupClosed(object sender, EventArgs e) private void PopupClosed(object sender, EventArgs e)
{ {
_subscriptionsOnOpen?.Dispose(); _subscriptionsOnOpen.Clear();
_subscriptionsOnOpen = null;
if (CanFocus(this)) if (CanFocus(this))
{ {
@ -316,20 +318,34 @@ namespace Avalonia.Controls
{ {
TryFocusSelectedItem(); TryFocusSelectedItem();
_subscriptionsOnOpen?.Dispose(); _subscriptionsOnOpen.Clear();
_subscriptionsOnOpen = null;
var toplevel = this.GetVisualRoot() as TopLevel; var toplevel = this.GetVisualRoot() as TopLevel;
if (toplevel != null) if (toplevel != null)
{ {
_subscriptionsOnOpen = toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) => toplevel.AddDisposableHandler(PointerWheelChangedEvent, (s, ev) =>
{ {
//eat wheel scroll event outside dropdown popup while it's open //eat wheel scroll event outside dropdown popup while it's open
if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel) if (IsDropDownOpen && (ev.Source as IVisual).GetVisualRoot() == toplevel)
{ {
ev.Handled = true; ev.Handled = true;
} }
}, Interactivity.RoutingStrategies.Tunnel); }, Interactivity.RoutingStrategies.Tunnel).DisposeWith(_subscriptionsOnOpen);
}
this.GetObservable(IsVisibleProperty).Subscribe(IsVisibleChanged).DisposeWith(_subscriptionsOnOpen);
foreach (var parent in this.GetVisualAncestors().OfType<IControl>())
{
parent.GetObservable(IsVisibleProperty).Subscribe(IsVisibleChanged).DisposeWith(_subscriptionsOnOpen);
}
}
private void IsVisibleChanged(bool isVisible)
{
if (!isVisible && IsDropDownOpen)
{
IsDropDownOpen = false;
} }
} }

38
src/Avalonia.Controls/Mixins/DisposableMixin.cs

@ -0,0 +1,38 @@
using System;
using System.Reactive.Disposables;
namespace Avalonia.Controls.Mixins
{
/// <summary>
/// Extension methods associated with the IDisposable interface.
/// </summary>
public static class DisposableMixin
{
/// <summary>
/// Ensures the provided disposable is disposed with the specified <see cref="CompositeDisposable"/>.
/// </summary>
/// <typeparam name="T">
/// The type of the disposable.
/// </typeparam>
/// <param name="item">
/// The disposable we are going to want to be disposed by the CompositeDisposable.
/// </param>
/// <param name="compositeDisposable">
/// The <see cref="CompositeDisposable"/> to which <paramref name="item"/> will be added.
/// </param>
/// <returns>
/// The disposable.
/// </returns>
public static T DisposeWith<T>(this T item, CompositeDisposable compositeDisposable)
where T : IDisposable
{
if (compositeDisposable is null)
{
throw new ArgumentNullException(nameof(compositeDisposable));
}
compositeDisposable.Add(item);
return item;
}
}
}

41
src/Avalonia.Controls/Primitives/Popup.cs

@ -2,6 +2,7 @@ using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Controls.Primitives.PopupPositioning;
@ -393,18 +394,8 @@ namespace Avalonia.Controls.Primitives
var handlerCleanup = new CompositeDisposable(5); var handlerCleanup = new CompositeDisposable(5);
void DeferCleanup(IDisposable? disposable) popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty,
{ HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty).DisposeWith(handlerCleanup);
if (disposable is null)
{
return;
}
handlerCleanup.Add(disposable);
}
DeferCleanup(popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty,
HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty));
popupHost.SetChild(Child); popupHost.SetChild(Child);
((ISetLogicalParent)popupHost).SetParent(this); ((ISetLogicalParent)popupHost).SetParent(this);
@ -418,19 +409,19 @@ namespace Avalonia.Controls.Primitives
PlacementConstraintAdjustment, PlacementConstraintAdjustment,
PlacementRect); PlacementRect);
DeferCleanup(SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied, SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied,
(x, handler) => x.TemplateApplied += handler, (x, handler) => x.TemplateApplied += handler,
(x, handler) => x.TemplateApplied -= handler)); (x, handler) => x.TemplateApplied -= handler).DisposeWith(handlerCleanup);
if (topLevel is Window window) if (topLevel is Window window)
{ {
DeferCleanup(SubscribeToEventHandler<Window, EventHandler>(window, WindowDeactivated, SubscribeToEventHandler<Window, EventHandler>(window, WindowDeactivated,
(x, handler) => x.Deactivated += handler, (x, handler) => x.Deactivated += handler,
(x, handler) => x.Deactivated -= handler)); (x, handler) => x.Deactivated -= handler).DisposeWith(handlerCleanup);
DeferCleanup(SubscribeToEventHandler<IWindowImpl, Action>(window.PlatformImpl, WindowLostFocus, SubscribeToEventHandler<IWindowImpl, Action>(window.PlatformImpl, WindowLostFocus,
(x, handler) => x.LostFocus += handler, (x, handler) => x.LostFocus += handler,
(x, handler) => x.LostFocus -= handler)); (x, handler) => x.LostFocus -= handler).DisposeWith(handlerCleanup);
} }
else else
{ {
@ -438,13 +429,13 @@ namespace Avalonia.Controls.Primitives
if (parentPopupRoot?.Parent is Popup popup) if (parentPopupRoot?.Parent is Popup popup)
{ {
DeferCleanup(SubscribeToEventHandler<Popup, EventHandler<EventArgs>>(popup, ParentClosed, SubscribeToEventHandler<Popup, EventHandler<EventArgs>>(popup, ParentClosed,
(x, handler) => x.Closed += handler, (x, handler) => x.Closed += handler,
(x, handler) => x.Closed -= handler)); (x, handler) => x.Closed -= handler).DisposeWith(handlerCleanup);
} }
} }
DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick)); InputManager.Instance?.Process.Subscribe(ListenForNonClientClick).DisposeWith(handlerCleanup);
var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state => var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state =>
{ {
@ -466,17 +457,17 @@ namespace Avalonia.Controls.Primitives
dismissLayer.IsVisible = true; dismissLayer.IsVisible = true;
dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement; dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement;
DeferCleanup(Disposable.Create(() => Disposable.Create(() =>
{ {
dismissLayer.IsVisible = false; dismissLayer.IsVisible = false;
dismissLayer.InputPassThroughElement = null; dismissLayer.InputPassThroughElement = null;
})); }).DisposeWith(handlerCleanup);
DeferCleanup(SubscribeToEventHandler<LightDismissOverlayLayer, EventHandler<PointerPressedEventArgs>>( SubscribeToEventHandler<LightDismissOverlayLayer, EventHandler<PointerPressedEventArgs>>(
dismissLayer, dismissLayer,
PointerPressedDismissOverlay, PointerPressedDismissOverlay,
(x, handler) => x.PointerPressed += handler, (x, handler) => x.PointerPressed += handler,
(x, handler) => x.PointerPressed -= handler)); (x, handler) => x.PointerPressed -= handler).DisposeWith(handlerCleanup);
} }
} }

99
src/Avalonia.Controls/SystemDialog.cs

@ -4,30 +4,65 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls.Platform; using Avalonia.Controls.Platform;
#nullable enable
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary>
/// Base class for system file dialogs.
/// </summary>
public abstract class FileDialog : FileSystemDialog public abstract class FileDialog : FileSystemDialog
{ {
/// <summary>
/// Gets or sets a collection of filters which determine the types of files displayed in an
/// <see cref="OpenFileDialog"/> or an <see cref="SaveFileDialog"/>.
/// </summary>
public List<FileDialogFilter> Filters { get; set; } = new List<FileDialogFilter>(); public List<FileDialogFilter> Filters { get; set; } = new List<FileDialogFilter>();
public string InitialFileName { get; set; }
/// <summary>
/// Gets or sets initial file name that is displayed when the dialog is opened.
/// </summary>
public string? InitialFileName { get; set; }
} }
/// <summary>
/// Base class for system file and directory dialogs.
/// </summary>
public abstract class FileSystemDialog : SystemDialog public abstract class FileSystemDialog : SystemDialog
{ {
[Obsolete("Use Directory")] [Obsolete("Use Directory")]
public string InitialDirectory public string? InitialDirectory
{ {
get => Directory; get => Directory;
set => Directory = value; set => Directory = value;
} }
public string Directory { get; set; }
/// <summary>
/// Gets or sets the initial directory that will be displayed when the file system dialog
/// is opened.
/// </summary>
public string? Directory { get; set; }
} }
/// <summary>
/// Represents a system dialog that prompts the user to select a location for saving a file.
/// </summary>
public class SaveFileDialog : FileDialog public class SaveFileDialog : FileDialog
{ {
public string DefaultExtension { get; set; } /// <summary>
/// Gets or sets the default extension to be used to save the file (including the period ".").
/// </summary>
public string? DefaultExtension { get; set; }
public async Task<string> ShowAsync(Window parent) /// <summary>
/// Shows the save file dialog.
/// </summary>
/// <param name="parent">The parent window.</param>
/// <returns>
/// A task that on completion contains the full path of the save location, or null if the
/// dialog was canceled.
/// </returns>
public async Task<string?> ShowAsync(Window parent)
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
@ -37,11 +72,25 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Represents a system dialog that allows the user to select one or more files to open.
/// </summary>
public class OpenFileDialog : FileDialog public class OpenFileDialog : FileDialog
{ {
/// <summary>
/// Gets or sets a value indicating whether the user can select multiple files.
/// </summary>
public bool AllowMultiple { get; set; } public bool AllowMultiple { get; set; }
public Task<string[]> ShowAsync(Window parent) /// <summary>
/// Shows the open file dialog.
/// </summary>
/// <param name="parent">The parent window.</param>
/// <returns>
/// A task that on completion returns an array containing the full path to the selected
/// files, or null if the dialog was canceled.
/// </returns>
public Task<string[]?> ShowAsync(Window parent)
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
@ -49,15 +98,27 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Represents a system dialog that allows the user to select a directory.
/// </summary>
public class OpenFolderDialog : FileSystemDialog public class OpenFolderDialog : FileSystemDialog
{ {
[Obsolete("Use Directory")] [Obsolete("Use Directory")]
public string DefaultDirectory public string? DefaultDirectory
{ {
get => Directory; get => Directory;
set => Directory = value; set => Directory = value;
} }
public Task<string> ShowAsync(Window parent)
/// <summary>
/// Shows the open folder dialog.
/// </summary>
/// <param name="parent">The parent window.</param>
/// <returns>
/// A task that on completion returns the full path of the selected directory, or null if the
/// dialog was canceled.
/// </returns>
public Task<string?> ShowAsync(Window parent)
{ {
if(parent == null) if(parent == null)
throw new ArgumentNullException(nameof(parent)); throw new ArgumentNullException(nameof(parent));
@ -65,14 +126,32 @@ namespace Avalonia.Controls
} }
} }
/// <summary>
/// Base class for system dialogs.
/// </summary>
public abstract class SystemDialog public abstract class SystemDialog
{ {
public string Title { get; set; } /// <summary>
/// Gets or sets the dialog title.
/// </summary>
public string? Title { get; set; }
} }
/// <summary>
/// Represents a filter in an <see cref="OpenFileDialog"/> or an <see cref="SaveFileDialog"/>.
/// </summary>
public class FileDialogFilter public class FileDialogFilter
{ {
public string Name { get; set; } /// <summary>
/// Gets or sets the name of the filter, e.g. ("Text files (.txt)").
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets or sets a list of file extensions matched by the filter (e.g. "txt" or "*" for all
/// files).
/// </summary>
public List<string> Extensions { get; set; } = new List<string>(); public List<string> Extensions { get; set; } = new List<string>();
} }
} }

11
src/Avalonia.Input/Navigation/TabNavigation.cs

@ -234,7 +234,7 @@ namespace Avalonia.Input.Navigation
// Return the first visible element. // Return the first visible element.
var uiElement = e as InputElement; var uiElement = e as InputElement;
if (uiElement is null || uiElement.IsVisible) if (uiElement is null || IsVisibleAndEnabled(uiElement))
{ {
if (e is IVisual elementAsVisual) if (e is IVisual elementAsVisual)
{ {
@ -245,7 +245,7 @@ namespace Avalonia.Input.Navigation
{ {
if (children[i] is InputElement ie) if (children[i] is InputElement ie)
{ {
if (ie.IsVisible) if (IsVisibleAndEnabled(ie))
return ie; return ie;
else else
{ {
@ -270,7 +270,7 @@ namespace Avalonia.Input.Navigation
// Return the last visible element. // Return the last visible element.
var uiElement = e as InputElement; var uiElement = e as InputElement;
if (uiElement == null || uiElement.IsVisible) if (uiElement == null || IsVisibleAndEnabled(uiElement))
{ {
var elementAsVisual = e as IVisual; var elementAsVisual = e as IVisual;
@ -283,7 +283,7 @@ namespace Avalonia.Input.Navigation
{ {
if (children[i] is InputElement ie) if (children[i] is InputElement ie)
{ {
if (ie.IsVisible) if (IsVisibleAndEnabled(ie))
return ie; return ie;
else else
{ {
@ -600,7 +600,7 @@ namespace Avalonia.Input.Navigation
var vchild = children[i]; var vchild = children[i];
if (vchild == elementAsVisual) if (vchild == elementAsVisual)
break; break;
if (vchild.IsVisible == true && vchild is IInputElement ie) if (vchild is IInputElement ie && IsVisibleAndEnabled(ie))
prev = ie; prev = ie;
} }
return prev; return prev;
@ -668,5 +668,6 @@ namespace Avalonia.Input.Navigation
} }
private static bool IsTabStopOrGroup(IInputElement e) => IsTabStop(e) || IsGroup(e); private static bool IsTabStopOrGroup(IInputElement e) => IsTabStop(e) || IsGroup(e);
private static bool IsVisibleAndEnabled(IInputElement e) => e.IsVisible && e.IsEnabled;
} }
} }

1
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -12,6 +12,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\Rx.props" /> <Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\SharpDX.props" /> <Import Project="..\..\..\build\SharpDX.props" />

3
src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs

@ -1,4 +1,5 @@
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Win32;
using Avalonia.Win32.Interop; using Avalonia.Win32.Interop;
using SharpDX; using SharpDX;
using SharpDX.DXGI; using SharpDX.DXGI;
@ -21,7 +22,7 @@ namespace Avalonia.Direct2D1
protected override Size2F GetWindowDpi() protected override Size2F GetWindowDpi()
{ {
if (UnmanagedMethods.ShCoreAvailable) if (UnmanagedMethods.ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{ {
uint dpix, dpiy; uint dpix, dpiy;

1
src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs

@ -150,6 +150,7 @@ namespace Avalonia.Win32.Interop.Wpf
if (_image == null || _oldDpi.X != dpi.X || _oldDpi.Y != dpi.Y) if (_image == null || _oldDpi.X != dpi.X || _oldDpi.Y != dpi.Y)
{ {
_image = new D3DImage(dpi.X, dpi.Y); _image = new D3DImage(dpi.X, dpi.Y);
_oldDpi = dpi;
} }
_impl.ImageSource = _image; _impl.ImageSource = _image;

2
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -87,7 +87,7 @@ namespace Avalonia.Win32
private Vector GetCurrentDpi() private Vector GetCurrentDpi()
{ {
if (UnmanagedMethods.ShCoreAvailable) if (UnmanagedMethods.ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{ {
var monitor = var monitor =
UnmanagedMethods.MonitorFromWindow(_hwnd, UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST); UnmanagedMethods.MonitorFromWindow(_hwnd, UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);

7
src/Windows/Avalonia.Win32/PlatformConstants.cs

@ -1,8 +1,13 @@
using System;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
static class PlatformConstants public static class PlatformConstants
{ {
public const string WindowHandleType = "HWND"; public const string WindowHandleType = "HWND";
public const string CursorHandleType = "HCURSOR"; public const string CursorHandleType = "HCURSOR";
public static readonly Version Windows8 = new Version(6, 2);
public static readonly Version Windows7 = new Version(6, 1);
} }
} }

6
src/Windows/Avalonia.Win32/SystemDialogImpl.cs

@ -19,7 +19,7 @@ namespace Avalonia.Win32
var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero; var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero;
return Task.Factory.StartNew(() => return Task.Factory.StartNew(() =>
{ {
var result = Array.Empty<string>(); string[] result = default;
Guid clsid = dialog is OpenFileDialog ? UnmanagedMethods.ShellIds.OpenFileDialog : UnmanagedMethods.ShellIds.SaveFileDialog; Guid clsid = dialog is OpenFileDialog ? UnmanagedMethods.ShellIds.OpenFileDialog : UnmanagedMethods.ShellIds.SaveFileDialog;
Guid iid = UnmanagedMethods.ShellIds.IFileDialog; Guid iid = UnmanagedMethods.ShellIds.IFileDialog;
@ -100,7 +100,7 @@ namespace Avalonia.Win32
{ {
return Task.Factory.StartNew(() => return Task.Factory.StartNew(() =>
{ {
string result = string.Empty; string result = default;
var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero; var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero;
Guid clsid = UnmanagedMethods.ShellIds.OpenFileDialog; Guid clsid = UnmanagedMethods.ShellIds.OpenFileDialog;
@ -164,7 +164,7 @@ namespace Avalonia.Win32
} }
} }
} }
return ""; return default;
} }
} }
} }

4
src/Windows/Avalonia.Win32/Win32GlManager.cs

@ -1,4 +1,3 @@
using System;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl; using Avalonia.OpenGL.Egl;
@ -9,7 +8,6 @@ namespace Avalonia.Win32
{ {
static class Win32GlManager static class Win32GlManager
{ {
private static readonly Version Windows7 = new Version(6, 1);
public static void Initialize() public static void Initialize()
{ {
@ -22,7 +20,7 @@ namespace Avalonia.Win32
return wgl; return wgl;
} }
if (opts?.AllowEglInitialization ?? Win32Platform.WindowsVersion > Windows7) if (opts?.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7)
{ {
var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay()); var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());

2
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -94,7 +94,7 @@ namespace Avalonia
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
class Win32Platform : IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform, IPlatformIconLoader, IPlatformLifetimeEventsImpl public class Win32Platform : IPlatformThreadingInterface, IPlatformSettings, IWindowingPlatform, IPlatformIconLoader, IPlatformLifetimeEventsImpl
{ {
private static readonly Win32Platform s_instance = new Win32Platform(); private static readonly Win32Platform s_instance = new Win32Platform();
private static Thread _uiThread; private static Thread _uiThread;

4
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -764,8 +764,8 @@ namespace Avalonia.Win32
RegisterTouchWindow(_hwnd, 0); RegisterTouchWindow(_hwnd, 0);
} }
if (ShCoreAvailable) if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{ {
var monitor = MonitorFromWindow( var monitor = MonitorFromWindow(
_hwnd, _hwnd,
MONITOR.MONITOR_DEFAULTTONEAREST); MONITOR.MONITOR_DEFAULTTONEAREST);

1
tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs

@ -10,7 +10,6 @@ using Avalonia.Styling;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
using Xunit; using Xunit;
using System;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Factory = System.Func<int, System.Action<object>, Avalonia.Controls.Window, Avalonia.AvaloniaObject>; using Factory = System.Func<int, System.Action<object>, Avalonia.Controls.Window, Avalonia.AvaloniaObject>;

3
tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs

@ -2,7 +2,6 @@
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.UnitTests; using Avalonia.UnitTests;
using Moq; using Moq;
using Xunit; using Xunit;
@ -126,7 +125,7 @@ namespace Avalonia.Input.UnitTests
{ {
private readonly Action _action; private readonly Action _action;
public DelegateCommand(Action action) => _action = action; public DelegateCommand(Action action) => _action = action;
public event EventHandler CanExecuteChanged; public event EventHandler CanExecuteChanged { add { } remove { } }
public bool CanExecute(object parameter) => true; public bool CanExecute(object parameter) => true;
public void Execute(object parameter) => _action(); public void Execute(object parameter) => _action();
} }

27
tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs

@ -1225,5 +1225,32 @@ namespace Avalonia.Input.UnitTests
"Button2", "Button3", "Button5", "Button1", "Button6", "Button4" "Button2", "Button3", "Button5", "Button1", "Button6", "Button4"
}, result); }, result);
} }
[Fact]
public void Cannot_Focus_Child_Of_Disabled_Control()
{
Button start;
Button expected;
var top = new StackPanel
{
[KeyboardNavigation.TabNavigationProperty] = KeyboardNavigationMode.Cycle,
Children =
{
(start = new Button { Name = "Button1" }),
new Border
{
IsEnabled = false,
Child = new Button { Name = "Button2" },
},
(expected = new Button { Name = "Button3" }),
}
};
var current = (IInputElement)start;
var result = KeyboardNavigationHandler.GetNext(current, NavigationDirection.Next);
Assert.Same(expected, result);
}
} }
} }

2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@ -923,7 +923,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public bool HasResources => true; public bool HasResources => true;
public List<object> RequestedResources { get; } = new List<object>(); public List<object> RequestedResources { get; } = new List<object>();
public event EventHandler OwnerChanged; public event EventHandler OwnerChanged { add { } remove { } }
public void AddOwner(IResourceHost owner) => Owner = owner; public void AddOwner(IResourceHost owner) => Owner = owner;
public void RemoveOwner(IResourceHost owner) => Owner = null; public void RemoveOwner(IResourceHost owner) => Owner = null;

Loading…
Cancel
Save