diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 14fe60ab0b..7a6e7dc72f 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -641,6 +641,7 @@ private:
[Window setCanBecomeKeyAndMain];
[Window disableCursorRects];
[Window setTabbingMode:NSWindowTabbingModeDisallowed];
+ [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
}
void HideOrShowTrafficLights ()
@@ -1091,14 +1092,7 @@ private:
{
_fullScreenActive = true;
- [Window setHasShadow:YES];
- [Window setTitleVisibility:NSWindowTitleVisible];
- [Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
-
- Window.styleMask = Window.styleMask | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;
- Window.styleMask = Window.styleMask & ~NSWindowStyleMaskFullSizeContentView;
-
[Window toggleFullScreen:nullptr];
}
@@ -1672,6 +1666,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
switch(event.buttonNumber)
{
+ case 2:
case 3:
_isMiddlePressed = true;
[self mouseEvent:event withType:MiddleButtonDown];
@@ -1704,6 +1699,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
switch(event.buttonNumber)
{
+ case 2:
case 3:
_isMiddlePressed = false;
[self mouseEvent:event withType:MiddleButtonUp];
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index 2c7f34c5be..2f1cb2888e 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaList.cs
@@ -280,8 +280,8 @@ namespace Avalonia.Collections
///
/// Gets a range of items from the collection.
///
- /// The first index to remove.
- /// The number of items to remove.
+ /// The zero-based index at which the range starts.
+ /// The number of elements in the range.
public IEnumerable GetRange(int index, int count)
{
return _inner.GetRange(index, count);
@@ -455,7 +455,7 @@ namespace Avalonia.Collections
}
///
- /// Ensures that the capacity of the list is at least .
+ /// Ensures that the capacity of the list is at least .
///
/// The capacity.
public void EnsureCapacity(int capacity)
diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs
index 9d8ebbfac1..587dd228a3 100644
--- a/src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs
+++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs
@@ -83,8 +83,9 @@ namespace Avalonia.Collections
if (key == null)
key = item;
- if (_valueConverter != null)
- key = _valueConverter.Convert(key, typeof(object), level, culture);
+ var valueConverter = ValueConverter;
+ if (valueConverter != null)
+ key = valueConverter.Convert(key, typeof(object), level, culture);
return key;
}
@@ -99,6 +100,8 @@ namespace Avalonia.Collections
}
public override string PropertyName => _propertyPath;
+ public IValueConverter ValueConverter { get => _valueConverter; set => _valueConverter = value; }
+
private Type GetPropertyType(object o)
{
return o.GetType().GetNestedPropertyType(_propertyPath);
diff --git a/src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs b/src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs
index 6ac77fbb99..1d1a595ccf 100644
--- a/src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs
+++ b/src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs
@@ -1,10 +1,8 @@
using Avalonia.Data;
using Avalonia.Reactive;
using System;
-using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Reactive.Subjects;
-using System.Text;
namespace Avalonia.Controls.Utils
{
@@ -67,11 +65,14 @@ namespace Avalonia.Controls.Utils
private void SetSourceValue(object value)
{
- _settingSourceValue = true;
+ if (!_settingSourceValue)
+ {
+ _settingSourceValue = true;
- _sourceSubject.OnNext(value);
+ _sourceSubject.OnNext(value);
- _settingSourceValue = false;
+ _settingSourceValue = false;
+ }
}
private void SetControlValue(object value)
{
@@ -157,4 +158,4 @@ namespace Avalonia.Controls.Utils
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index 5a6e78f441..0e946126ea 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -2094,7 +2094,21 @@ namespace Avalonia.Controls
bool inResults = !(stringFiltering || objectFiltering);
if (!inResults)
{
- inResults = stringFiltering ? TextFilter(text, FormatValue(item)) : ItemFilter(text, item);
+ if (stringFiltering)
+ {
+ inResults = TextFilter(text, FormatValue(item));
+ }
+ else
+ {
+ if (ItemFilter is null)
+ {
+ throw new Exception("ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom");
+ }
+ else
+ {
+ inResults = ItemFilter(text, item);
+ }
+ }
}
if (view_count > view_index && inResults && _view[view_index] == item)
diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs
index 0eade8d6df..9eae928eeb 100644
--- a/src/Avalonia.Controls/TextBox.cs
+++ b/src/Avalonia.Controls/TextBox.cs
@@ -145,6 +145,18 @@ namespace Avalonia.Controls
(o, v) => o.UndoLimit = v,
unsetValue: -1);
+ public static readonly RoutedEvent CopyingToClipboardEvent =
+ RoutedEvent.Register(
+ "CopyingToClipboard", RoutingStrategies.Bubble);
+
+ public static readonly RoutedEvent CuttingToClipboardEvent =
+ RoutedEvent.Register(
+ "CuttingToClipboard", RoutingStrategies.Bubble);
+
+ public static readonly RoutedEvent PastingFromClipboardEvent =
+ RoutedEvent.Register(
+ "PastingFromClipboard", RoutingStrategies.Bubble);
+
readonly struct UndoRedoState : IEquatable
{
public string Text { get; }
@@ -500,6 +512,24 @@ namespace Avalonia.Controls
}
}
+ public event EventHandler CopyingToClipboard
+ {
+ add => AddHandler(CopyingToClipboardEvent, value);
+ remove => RemoveHandler(CopyingToClipboardEvent, value);
+ }
+
+ public event EventHandler CuttingToClipboard
+ {
+ add => AddHandler(CuttingToClipboardEvent, value);
+ remove => RemoveHandler(CuttingToClipboardEvent, value);
+ }
+
+ public event EventHandler PastingFromClipboard
+ {
+ add => AddHandler(PastingFromClipboardEvent, value);
+ remove => RemoveHandler(PastingFromClipboardEvent, value);
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_presenter = e.NameScope.Get("PART_TextPresenter");
@@ -638,27 +668,54 @@ namespace Avalonia.Controls
public async void Cut()
{
var text = GetSelection();
- if (text is null) return;
+ if (string.IsNullOrEmpty(text))
+ {
+ return;
+ }
- SnapshotUndoRedo();
- Copy();
- DeleteSelection();
+ var eventArgs = new RoutedEventArgs(CuttingToClipboardEvent);
+ RaiseEvent(eventArgs);
+ if (!eventArgs.Handled)
+ {
+ SnapshotUndoRedo();
+ await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))
+ .SetTextAsync(text);
+ DeleteSelection();
+ }
}
public async void Copy()
{
var text = GetSelection();
- if (text is null) return;
+ if (string.IsNullOrEmpty(text))
+ {
+ return;
+ }
- await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))
- .SetTextAsync(text);
+ var eventArgs = new RoutedEventArgs(CopyingToClipboardEvent);
+ RaiseEvent(eventArgs);
+ if (!eventArgs.Handled)
+ {
+ await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)))
+ .SetTextAsync(text);
+ }
}
public async void Paste()
{
+ var eventArgs = new RoutedEventArgs(PastingFromClipboardEvent);
+ RaiseEvent(eventArgs);
+ if (eventArgs.Handled)
+ {
+ return;
+ }
+
var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync();
- if (text is null) return;
+ if (string.IsNullOrEmpty(text))
+ {
+ return;
+ }
SnapshotUndoRedo();
HandleTextInput(text);
diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs
index 5c4af68d79..5082265ea6 100644
--- a/src/Avalonia.Input/AccessKeyHandler.cs
+++ b/src/Avalonia.Input/AccessKeyHandler.cs
@@ -157,10 +157,9 @@ namespace Avalonia.Input
_restoreFocusElement?.Focus();
_restoreFocusElement = null;
+
+ e.Handled = true;
}
-
- // We always handle the Alt key.
- e.Handled = true;
}
else if (_altIsDown)
{
diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Input/Navigation/TabNavigation.cs
index ed7df67bf2..c8290cb3b7 100644
--- a/src/Avalonia.Input/Navigation/TabNavigation.cs
+++ b/src/Avalonia.Input/Navigation/TabNavigation.cs
@@ -234,7 +234,7 @@ namespace Avalonia.Input.Navigation
// Return the first visible element.
var uiElement = e as InputElement;
- if (uiElement is null || uiElement.IsVisible)
+ if (uiElement is null || IsVisibleAndEnabled(uiElement))
{
if (e is IVisual elementAsVisual)
{
@@ -245,7 +245,7 @@ namespace Avalonia.Input.Navigation
{
if (children[i] is InputElement ie)
{
- if (ie.IsVisible)
+ if (IsVisibleAndEnabled(ie))
return ie;
else
{
@@ -270,7 +270,7 @@ namespace Avalonia.Input.Navigation
// Return the last visible element.
var uiElement = e as InputElement;
- if (uiElement == null || uiElement.IsVisible)
+ if (uiElement == null || IsVisibleAndEnabled(uiElement))
{
var elementAsVisual = e as IVisual;
@@ -283,7 +283,7 @@ namespace Avalonia.Input.Navigation
{
if (children[i] is InputElement ie)
{
- if (ie.IsVisible)
+ if (IsVisibleAndEnabled(ie))
return ie;
else
{
@@ -600,7 +600,7 @@ namespace Avalonia.Input.Navigation
var vchild = children[i];
if (vchild == elementAsVisual)
break;
- if (vchild.IsVisible == true && vchild is IInputElement ie)
+ if (vchild is IInputElement ie && IsVisibleAndEnabled(ie))
prev = ie;
}
return prev;
@@ -668,5 +668,6 @@ namespace Avalonia.Input.Navigation
}
private static bool IsTabStopOrGroup(IInputElement e) => IsTabStop(e) || IsGroup(e);
+ private static bool IsVisibleAndEnabled(IInputElement e) => e.IsVisible && e.IsEnabled;
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
index cda95d2ebb..8533e1e176 100644
--- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
+++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
index 589f85f208..0597e88ed6 100644
--- a/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
+++ b/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
@@ -1,4 +1,5 @@
using Avalonia.Platform;
+using Avalonia.Win32;
using Avalonia.Win32.Interop;
using SharpDX;
using SharpDX.DXGI;
@@ -21,7 +22,7 @@ namespace Avalonia.Direct2D1
protected override Size2F GetWindowDpi()
{
- if (UnmanagedMethods.ShCoreAvailable)
+ if (UnmanagedMethods.ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{
uint dpix, dpiy;
diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
index 5b04c5d7ff..bc0a399d7f 100644
--- a/src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs
+++ b/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)
{
_image = new D3DImage(dpi.X, dpi.Y);
+ _oldDpi = dpi;
}
_impl.ImageSource = _image;
diff --git a/src/Windows/Avalonia.Win32/FramebufferManager.cs b/src/Windows/Avalonia.Win32/FramebufferManager.cs
index 699dc7c25d..0240ac9701 100644
--- a/src/Windows/Avalonia.Win32/FramebufferManager.cs
+++ b/src/Windows/Avalonia.Win32/FramebufferManager.cs
@@ -87,7 +87,7 @@ namespace Avalonia.Win32
private Vector GetCurrentDpi()
{
- if (UnmanagedMethods.ShCoreAvailable)
+ if (UnmanagedMethods.ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
{
var monitor =
UnmanagedMethods.MonitorFromWindow(_hwnd, UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
diff --git a/src/Windows/Avalonia.Win32/PlatformConstants.cs b/src/Windows/Avalonia.Win32/PlatformConstants.cs
index af1eca29be..9dd4780637 100644
--- a/src/Windows/Avalonia.Win32/PlatformConstants.cs
+++ b/src/Windows/Avalonia.Win32/PlatformConstants.cs
@@ -1,8 +1,13 @@
+using System;
+
namespace Avalonia.Win32
{
- static class PlatformConstants
+ public static class PlatformConstants
{
public const string WindowHandleType = "HWND";
public const string CursorHandleType = "HCURSOR";
+
+ public static readonly Version Windows8 = new Version(6, 2);
+ public static readonly Version Windows7 = new Version(6, 1);
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index e159ba3b78..e70ea52106 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -1,4 +1,3 @@
-using System;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
@@ -9,7 +8,6 @@ namespace Avalonia.Win32
{
static class Win32GlManager
{
- private static readonly Version Windows7 = new Version(6, 1);
public static void Initialize()
{
@@ -22,7 +20,7 @@ namespace Avalonia.Win32
return wgl;
}
- if (opts?.AllowEglInitialization ?? Win32Platform.WindowsVersion > Windows7)
+ if (opts?.AllowEglInitialization ?? Win32Platform.WindowsVersion > PlatformConstants.Windows7)
{
var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index a881c45cd0..c011a458c3 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -94,7 +94,7 @@ namespace Avalonia
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 Thread _uiThread;
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 0b7bd13082..3b7d3efa2f 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -764,8 +764,8 @@ namespace Avalonia.Win32
RegisterTouchWindow(_hwnd, 0);
}
- if (ShCoreAvailable)
- {
+ if (ShCoreAvailable && Win32Platform.WindowsVersion > PlatformConstants.Windows8)
+ {
var monitor = MonitorFromWindow(
_hwnd,
MONITOR.MONITOR_DEFAULTTONEAREST);
diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
index b346fca330..c8bd289e54 100644
--- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs
@@ -105,6 +105,16 @@ namespace Avalonia.Controls.UnitTests
});
}
+ [Fact]
+ public void Custom_FilterMode_Without_ItemFilter_Setting_Throws_Exception()
+ {
+ RunTest((control, textbox) =>
+ {
+ control.FilterMode = AutoCompleteFilterMode.Custom;
+ Assert.Throws(() => { control.Text = "a"; });
+ });
+ }
+
[Fact]
public void Text_Completion_Via_Text_Property()
{
diff --git a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
index 2a3db9992c..b0eb694944 100644
--- a/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Utils/HotKeyManagerTests.cs
@@ -10,7 +10,6 @@ using Avalonia.Styling;
using Avalonia.UnitTests;
using Moq;
using Xunit;
-using System;
using Avalonia.Input.Raw;
using Factory = System.Func, Avalonia.Controls.Window, Avalonia.AvaloniaObject>;
diff --git a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
index 7730cee78c..c354dbe72e 100644
--- a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
+++ b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
@@ -2,7 +2,6 @@
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Input.Raw;
-using Avalonia.Interactivity;
using Avalonia.UnitTests;
using Moq;
using Xunit;
@@ -126,7 +125,7 @@ namespace Avalonia.Input.UnitTests
{
private readonly 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 void Execute(object parameter) => _action();
}
diff --git a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs b/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs
index edcbf75a1d..9a117bb71d 100644
--- a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs
+++ b/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs
@@ -1225,5 +1225,32 @@ namespace Avalonia.Input.UnitTests
"Button2", "Button3", "Button5", "Button1", "Button6", "Button4"
}, 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);
+ }
}
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
index e7f0230254..592dbfc0d1 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
@@ -923,7 +923,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public bool HasResources => true;
public List