diff --git a/Documentation/build.md b/Documentation/build.md
index 2f59146a48..5f75290424 100644
--- a/Documentation/build.md
+++ b/Documentation/build.md
@@ -60,15 +60,10 @@ git submodule update --init --recursive
### Build native libraries (macOS only)
-On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). The steps to get this working correctly are:
-- (for revisions after 2 Nov 2020) Run `./build.sh GenerateCppHeaders` to generate `avalonia-native.h` from `avn.idl`
-- Navigate to the Avalonia/native/Avalonia.Native/src/OSX folder and open the `Avalonia.Native.OSX.xcodeproj` project
-- Build the library via the Product->Build menu. This will generate binaries in your local path under ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-*guid* where "guid" is uniquely generated every time you build.
-- Manually install the native library by copying it from the build artifacts folder into the shared dynamic library path:
+On macOS it is necessary to build and manually install the respective native libraries using [Xcode](https://developer.apple.com/xcode/). Execute the build script in the root project with the `CompileNative` task. It will build the headers, build the libraries, and place them in the appropriate place to allow .NET to find them at compilation and run time.
-```
-cd ~/Library/Developer/Xcode/DerivedData/Avalonia.Native.OSX-[guid]/Build/Products/Debug
-cp libAvalonia.Native.OSX.dylib /usr/local/lib/libAvaloniaNative.dylib
+```bash
+./build.sh CompileNative
```
### Build and Run Avalonia
diff --git a/build/EmbedXaml.props b/build/EmbedXaml.props
index 7ce0366dea..0bb8da4f47 100644
--- a/build/EmbedXaml.props
+++ b/build/EmbedXaml.props
@@ -4,8 +4,9 @@
%(Filename)
+
Designer
-
\ No newline at end of file
+
diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm
index f93436d157..e83bf53331 100644
--- a/native/Avalonia.Native/src/OSX/platformthreading.mm
+++ b/native/Avalonia.Native/src/OSX/platformthreading.mm
@@ -101,7 +101,7 @@ public:
virtual bool GetCurrentThreadIsLoopThread() override
{
- return [[NSThread currentThread] isMainThread];
+ return [NSThread isMainThread];
}
virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override
{
diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm
index 93a33bbbb0..00b6dab219 100644
--- a/native/Avalonia.Native/src/OSX/rendertarget.mm
+++ b/native/Avalonia.Native/src/OSX/rendertarget.mm
@@ -2,6 +2,7 @@
#include "rendertarget.h"
#import
#import
+#import
#include
#include
@@ -143,13 +144,17 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
return _layer;
}
-- (void)resize:(AvnPixelSize)size withScale: (float) scale;{
+- (void)resize:(AvnPixelSize)size withScale: (float) scale{
@synchronized (lock) {
if(surface == nil
|| surface->size.Width != size.Width
|| surface->size.Height != size.Height
|| surface->scale != scale)
+ {
surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()];
+
+ [self updateLayer];
+ }
}
}
@@ -159,12 +164,15 @@ static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* ta
@synchronized (lock) {
if(_layer == nil)
return;
+ [CATransaction begin];
[_layer setContents: nil];
if(surface != nil)
{
[_layer setContentsScale: surface->scale];
[_layer setContents: (__bridge IOSurface*) surface->surface];
}
+ [CATransaction commit];
+ [CATransaction flush];
}
}
else
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index 22f4e9be1f..020fb2fff3 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -23,7 +23,7 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml")
+ Source = new Uri("avares://Avalonia.Themes.Fluent/FluentDark.xaml")
},
DataGridFluent
};
@@ -32,7 +32,7 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{
- Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml")
+ Source = new Uri("avares://Avalonia.Themes.Fluent/FluentLight.xaml")
},
DataGridFluent
};
diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml
index 8e4c97b7f0..5251a2fa55 100644
--- a/samples/ControlCatalog/DecoratedWindow.xaml
+++ b/samples/ControlCatalog/DecoratedWindow.xaml
@@ -6,25 +6,21 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml
index 97bd88f5e4..6a70bb082f 100644
--- a/samples/ControlCatalog/MainWindow.xaml
+++ b/samples/ControlCatalog/MainWindow.xaml
@@ -16,47 +16,39 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs
index a316321cd1..723351ae57 100644
--- a/samples/ControlCatalog/MainWindow.xaml.cs
+++ b/samples/ControlCatalog/MainWindow.xaml.cs
@@ -67,7 +67,7 @@ namespace ControlCatalog
if (Application.Current.Styles.Contains(App.FluentDark)
|| Application.Current.Styles.Contains(App.FluentLight))
{
- var theme = new Avalonia.Themes.Fluent.FluentTheme();
+ var theme = new Avalonia.Themes.Fluent.Controls.FluentControls();
theme.TryGetResource("Button", out _);
}
else
diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
index cf6c771e34..49921fb7f6 100644
--- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Dialogs;
+using Avalonia.Layout;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
@@ -112,11 +113,29 @@ namespace ControlCatalog.Pages
private Window CreateSampleWindow()
{
- var window = new Window();
- window.Height = 200;
- window.Width = 200;
- window.Content = new TextBlock { Text = "Hello world!" };
- window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
+ Button button;
+
+ var window = new Window
+ {
+ Height = 200,
+ Width = 200,
+ Content = new StackPanel
+ {
+ Spacing = 4,
+ Children =
+ {
+ new TextBlock { Text = "Hello world!" },
+ (button = new Button
+ {
+ HorizontalAlignment = HorizontalAlignment.Center,
+ Content = "Click to close"
+ })
+ }
+ },
+ WindowStartupLocation = WindowStartupLocation.CenterOwner
+ };
+
+ button.Click += (_, __) => window.Close();
return window;
}
diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml
index 61e4d2385b..eeeaf5b0ae 100644
--- a/samples/RenderDemo/App.xaml
+++ b/samples/RenderDemo/App.xaml
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.App">
-
+
diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml
index 699781eb94..a351ba93e9 100644
--- a/samples/Sandbox/App.axaml
+++ b/samples/Sandbox/App.axaml
@@ -3,6 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sandbox.App">
-
+
diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs
index d43b4e04bb..5681214222 100644
--- a/src/Avalonia.Base/Collections/AvaloniaList.cs
+++ b/src/Avalonia.Base/Collections/AvaloniaList.cs
@@ -63,6 +63,15 @@ namespace Avalonia.Collections
_inner = new List();
}
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// Initial list capacity.
+ public AvaloniaList(int capacity)
+ {
+ _inner = new List(capacity);
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -175,6 +184,15 @@ namespace Avalonia.Collections
set { this[index] = (T)value; }
}
+ ///
+ /// Gets or sets the total number of elements the internal data structure can hold without resizing.
+ ///
+ public int Capacity
+ {
+ get => _inner.Capacity;
+ set => _inner.Capacity = value;
+ }
+
///
/// Adds an item to the collection.
///
diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
index 0238446892..6e52b6770a 100644
--- a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
+++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+#nullable enable
namespace Avalonia.Utilities
{
@@ -9,12 +12,14 @@ namespace Avalonia.Utilities
/// Stored value type.
internal sealed class AvaloniaPropertyValueStore
{
+ // The last item in the list is always int.MaxValue.
+ private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } };
+
private Entry[] _entries;
public AvaloniaPropertyValueStore()
{
- // The last item in the list is always int.MaxValue
- _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } };
+ _entries = s_emptyEntries;
}
private (int, bool) TryFindEntry(int propertyId)
@@ -86,7 +91,7 @@ namespace Avalonia.Utilities
return (0, false);
}
- public bool TryGetValue(AvaloniaProperty property, out TValue value)
+ public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value)
{
(int index, bool found) = TryFindEntry(property.Id);
if (!found)
@@ -132,7 +137,18 @@ namespace Avalonia.Utilities
if (found)
{
- Entry[] entries = new Entry[_entries.Length - 1];
+ var newLength = _entries.Length - 1;
+
+ // Special case - one element left means that value store is empty so we can just reuse our "empty" array.
+ if (newLength == 1)
+ {
+ _entries = s_emptyEntries;
+
+ return;
+ }
+
+ var entries = new Entry[newLength];
+
int ix = 0;
for (int i = 0; i < _entries.Length; ++i)
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index bfac1ac1e1..90f6abc873 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -1,7 +1,7 @@
netstandard2.0
- netstandard2.0;netcoreapp2.0
+ netstandard2.0;netcoreapp3.1
exe
false
tools
@@ -81,6 +81,15 @@
Markup/%(RecursiveDir)%(FileName)%(Extension)
+
+ Markup/%(RecursiveDir)%(FileName)%(Extension)
+
+
+ Markup/%(RecursiveDir)%(FileName)%(Extension)
+
+
+ Markup/%(RecursiveDir)%(FileName)%(Extension)
+
diff --git a/src/Avalonia.Build.Tasks/SpanCompat.cs b/src/Avalonia.Build.Tasks/SpanCompat.cs
index d5c406293e..f8960f56ec 100644
--- a/src/Avalonia.Build.Tasks/SpanCompat.cs
+++ b/src/Avalonia.Build.Tasks/SpanCompat.cs
@@ -1,3 +1,4 @@
+#if !NETCOREAPP3_1
namespace System
{
// This is a hack to enable our span code to work inside MSBuild task without referencing System.Memory
@@ -63,6 +64,8 @@ namespace System
}
public override string ToString() => _length == 0 ? string.Empty : _s.Substring(_start, _length);
+
+ public static implicit operator ReadOnlySpan(char[] arr) => new ReadOnlySpan(new string(arr));
}
static class SpanCompatExtensions
@@ -71,3 +74,4 @@ namespace System
}
}
+#endif
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index bfd633c947..b59fd7abde 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -1405,8 +1405,11 @@ namespace Avalonia.Controls
break;
case Key.Enter:
- OnAdapterSelectionComplete(this, new RoutedEventArgs());
- e.Handled = true;
+ if (IsDropDownOpen)
+ {
+ OnAdapterSelectionComplete(this, new RoutedEventArgs());
+ e.Handled = true;
+ }
break;
default:
diff --git a/src/Avalonia.Controls/DefinitionList.cs b/src/Avalonia.Controls/DefinitionList.cs
index bb815171c0..ad0e2513c4 100644
--- a/src/Avalonia.Controls/DefinitionList.cs
+++ b/src/Avalonia.Controls/DefinitionList.cs
@@ -1,8 +1,9 @@
-using System;
+using System.Collections;
using System.Collections.Specialized;
-using System.Linq;
using Avalonia.Collections;
+#nullable enable
+
namespace Avalonia.Controls
{
public abstract class DefinitionList : AvaloniaList where T : DefinitionBase
@@ -14,44 +15,65 @@ namespace Avalonia.Controls
}
internal bool IsDirty = true;
- private Grid _parent;
+ private Grid? _parent;
- internal Grid Parent
+ internal Grid? Parent
{
get => _parent;
set => SetParent(value);
}
- private void SetParent(Grid value)
+ private void SetParent(Grid? value)
{
_parent = value;
- foreach (var pair in this.Select((definitions, index) => (definitions, index)))
+ var idx = 0;
+
+ foreach (T definition in this)
{
- pair.definitions.Parent = value;
- pair.definitions.Index = pair.index;
+ definition.Parent = value;
+ definition.Index = idx++;
}
}
internal void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
- foreach (var nI in this.Select((d, i) => (d, i)))
- nI.d._parentIndex = nI.i;
+ var idx = 0;
- foreach (var nD in e.NewItems?.Cast()
- ?? Enumerable.Empty())
+ foreach (T definition in this)
{
- nD.Parent = this.Parent;
- nD.OnEnterParentTree();
+ definition.Index = idx++;
}
+
+ UpdateDefinitionParent(e.NewItems, false);
+ UpdateDefinitionParent(e.OldItems, true);
+
+ IsDirty = true;
+ }
- foreach (var oD in e.OldItems?.Cast()
- ?? Enumerable.Empty())
+ private void UpdateDefinitionParent(IList? items, bool wasRemoved)
+ {
+ if (items is null)
{
- oD.OnExitParentTree();
+ return;
}
+
+ var count = items.Count;
- IsDirty = true;
+ for (var i = 0; i < count; i++)
+ {
+ var definition = (DefinitionBase) items[i];
+
+ if (wasRemoved)
+ {
+ definition.OnExitParentTree();
+ }
+ else
+ {
+ definition.Parent = Parent;
+ definition.OnEnterParentTree();
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs
index 023b108061..31d9a5c02e 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs
@@ -37,6 +37,6 @@ namespace Avalonia.Controls.Generators
///
/// Gets the index of the item in the collection.
///
- public int Index { get; internal set; }
+ public int Index { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/GridLength.cs b/src/Avalonia.Controls/GridLength.cs
index 57f308d59f..b8418949d9 100644
--- a/src/Avalonia.Controls/GridLength.cs
+++ b/src/Avalonia.Controls/GridLength.cs
@@ -8,7 +8,10 @@ namespace Avalonia.Controls
///
/// Defines the valid units for a .
///
- public enum GridUnitType
+#if !BUILDTASK
+ public
+#endif
+ enum GridUnitType
{
///
/// The row or column is auto-sized to fit its content.
@@ -29,7 +32,10 @@ namespace Avalonia.Controls
///
/// Holds the width or height of a 's column and row definitions.
///
- public struct GridLength : IEquatable
+#if !BUILDTASK
+ public
+#endif
+ struct GridLength : IEquatable
{
private readonly GridUnitType _type;
diff --git a/src/Avalonia.Controls/NativeMenuBar.cs b/src/Avalonia.Controls/NativeMenuBar.cs
index 63bb39108f..5d2e1087a7 100644
--- a/src/Avalonia.Controls/NativeMenuBar.cs
+++ b/src/Avalonia.Controls/NativeMenuBar.cs
@@ -1,7 +1,6 @@
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
-using Avalonia.Styling;
namespace Avalonia.Controls
{
@@ -16,7 +15,7 @@ namespace Avalonia.Controls
EnableMenuItemClickForwardingProperty.Changed.Subscribe(args =>
{
var item = (MenuItem)args.Sender;
- if (args.NewValue.Equals(true))
+ if (args.NewValue.GetValueOrDefault())
item.Click += OnMenuItemClick;
else
item.Click -= OnMenuItemClick;
diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs
index a0fec9e677..76197b62ad 100644
--- a/src/Avalonia.Controls/NativeMenuItem.cs
+++ b/src/Avalonia.Controls/NativeMenuItem.cs
@@ -2,6 +2,7 @@ using System;
using System.Windows.Input;
using Avalonia.Input;
using Avalonia.Media.Imaging;
+using Avalonia.Metadata;
using Avalonia.Utilities;
namespace Avalonia.Controls
@@ -62,6 +63,7 @@ namespace Avalonia.Controls
public static readonly DirectProperty MenuProperty =
AvaloniaProperty.RegisterDirect(nameof(Menu), o => o.Menu, (o, v) => o.Menu = v);
+ [Content]
public NativeMenu Menu
{
get => _menu;
@@ -151,7 +153,7 @@ namespace Avalonia.Controls
IsEnabled = _command?.CanExecute(null) ?? true;
}
- public bool HasClickHandlers => Clicked != null;
+ public bool HasClickHandlers => Click != null;
public ICommand Command
{
@@ -182,11 +184,21 @@ namespace Avalonia.Controls
set { SetValue(CommandParameterProperty, value); }
}
- public event EventHandler Clicked;
+ ///
+ /// Occurs when a is clicked.
+ ///
+ public event EventHandler Click;
+
+ [Obsolete("Use Click event.")]
+ public event EventHandler Clicked
+ {
+ add => Click += value;
+ remove => Click -= value;
+ }
void INativeMenuItemExporterEventsImplBridge.RaiseClicked()
{
- Clicked?.Invoke(this, new EventArgs());
+ Click?.Invoke(this, new EventArgs());
if (Command?.CanExecute(CommandParameter) == true)
{
diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
index a54d1ce308..5cbd3698b6 100644
--- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
+++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
@@ -231,7 +231,7 @@ namespace Avalonia.Controls.Platform
{
var direction = e.Key.ToNavigationDirection();
- if (direction.HasValue)
+ if (direction?.IsDirectional() == true)
{
if (item == null && _isContextMenu)
{
diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs
index a7fb7ae08c..d264ed76cc 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBar.cs
+++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the property.
///
public static readonly StyledProperty VisibilityProperty =
- AvaloniaProperty.Register(nameof(Visibility));
+ AvaloniaProperty.Register(nameof(Visibility), ScrollBarVisibility.Visible);
///
/// Defines the property.
diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs
index 6e08e78813..fe4b24099f 100644
--- a/src/Avalonia.Controls/Slider.cs
+++ b/src/Avalonia.Controls/Slider.cs
@@ -3,6 +3,7 @@ using Avalonia.Collections;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
+using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
@@ -94,6 +95,8 @@ namespace Avalonia.Controls
Thumb.DragStartedEvent.AddClassHandler((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e),
RoutingStrategies.Bubble);
+
+ ValueProperty.OverrideMetadata(new DirectPropertyMetadata(enableDataValidation: true));
}
///
@@ -225,6 +228,14 @@ namespace Avalonia.Controls
Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue;
}
+ protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value)
+ {
+ if (property == ValueProperty)
+ {
+ DataValidationErrors.SetError(this, value.Error);
+ }
+ }
+
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
diff --git a/src/Avalonia.Input/NavigationDirection.cs b/src/Avalonia.Input/NavigationDirection.cs
index 9b9af0b0a6..9bd6a8bb42 100644
--- a/src/Avalonia.Input/NavigationDirection.cs
+++ b/src/Avalonia.Input/NavigationDirection.cs
@@ -83,7 +83,7 @@ namespace Avalonia.Input
///
public static bool IsDirectional(this NavigationDirection direction)
{
- return direction > NavigationDirection.Previous ||
+ return direction > NavigationDirection.Previous &&
direction <= NavigationDirection.PageDown;
}
diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
index b192de95de..2e3408eca5 100644
--- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
+++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
@@ -60,7 +60,7 @@ namespace Avalonia.Native
Header = "About Avalonia",
};
- aboutItem.Clicked += async (sender, e) =>
+ aboutItem.Click += async (sender, e) =>
{
var dialog = new AboutAvaloniaDialog();
diff --git a/src/Avalonia.Native/DoubleClickHelper.cs b/src/Avalonia.Native/DoubleClickHelper.cs
new file mode 100644
index 0000000000..7618d6976a
--- /dev/null
+++ b/src/Avalonia.Native/DoubleClickHelper.cs
@@ -0,0 +1,31 @@
+using Avalonia.Platform;
+
+namespace Avalonia.Native
+{
+ internal class DoubleClickHelper
+ {
+ private int _clickCount;
+ private Rect _lastClickRect;
+ private ulong _lastClickTime;
+
+ public bool IsDoubleClick(
+ ulong timestamp,
+ Point p)
+ {
+ var settings = AvaloniaLocator.Current.GetService();
+ var doubleClickTime = settings.DoubleClickTime.TotalMilliseconds;
+
+ if (!_lastClickRect.Contains(p) || timestamp - _lastClickTime > doubleClickTime)
+ {
+ _clickCount = 0;
+ }
+
+ ++_clickCount;
+ _lastClickTime = timestamp;
+ _lastClickRect = new Rect(p, new Size())
+ .Inflate(new Thickness(settings.DoubleClickSize.Width / 2, settings.DoubleClickSize.Height / 2));
+
+ return _clickCount == 2;
+ }
+ }
+}
diff --git a/src/Avalonia.Native/Extensions.cs b/src/Avalonia.Native/Extensions.cs
new file mode 100644
index 0000000000..c0d90f7a85
--- /dev/null
+++ b/src/Avalonia.Native/Extensions.cs
@@ -0,0 +1,8 @@
+namespace Avalonia.Native
+{
+ internal static class Extensions
+ {
+ public static int AsComBool(this bool b) => b ? 1 : 0;
+ public static bool FromComBool(this int b) => b != 0;
+ }
+}
diff --git a/src/Avalonia.Native/IAvnMenuItem.cs b/src/Avalonia.Native/IAvnMenuItem.cs
index e2feffaa33..4c0c81394f 100644
--- a/src/Avalonia.Native/IAvnMenuItem.cs
+++ b/src/Avalonia.Native/IAvnMenuItem.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Native.Interop.Impl
private void UpdateTitle(string title) => SetTitle(title ?? "");
- private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked);
+ private void UpdateIsChecked(bool isChecked) => SetIsChecked(isChecked.AsComBool());
private void UpdateToggleType(NativeMenuItemToggleType toggleType)
{
diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs
index df69f2eafb..882d5a2ed3 100644
--- a/src/Avalonia.Native/PlatformThreadingInterface.cs
+++ b/src/Avalonia.Native/PlatformThreadingInterface.cs
@@ -33,9 +33,9 @@ namespace Avalonia.Native
_parent = parent;
}
- public void Signaled(int priority, bool priorityContainsMeaningfulValue)
+ public void Signaled(int priority, int priorityContainsMeaningfulValue)
{
- _parent.Signaled?.Invoke(priorityContainsMeaningfulValue ? (DispatcherPriority?)priority : null);
+ _parent.Signaled?.Invoke(priorityContainsMeaningfulValue.FromComBool() ? (DispatcherPriority?)priority : null);
}
}
@@ -50,7 +50,7 @@ namespace Avalonia.Native
_native.SetSignaledCallback(cb);
}
- public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread;
+ public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread.FromComBool();
public event Action Signaled;
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index 2f98385038..60c552a937 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -50,9 +50,9 @@ namespace Avalonia.Native
// NOP on Popup
}
- bool IAvnWindowEvents.Closing()
+ int IAvnWindowEvents.Closing()
{
- return true;
+ return true.AsComBool();
}
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
diff --git a/src/Avalonia.Native/PredicateCallback.cs b/src/Avalonia.Native/PredicateCallback.cs
index 1ed2ae36af..19c470bcb3 100644
--- a/src/Avalonia.Native/PredicateCallback.cs
+++ b/src/Avalonia.Native/PredicateCallback.cs
@@ -12,9 +12,9 @@ namespace Avalonia.Native
_predicate = predicate;
}
- bool IAvnPredicateCallback.Evaluate()
+ int IAvnPredicateCallback.Evaluate()
{
- return _predicate();
+ return _predicate().AsComBool();
}
}
}
diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs
index ae6da01388..7b4a001486 100644
--- a/src/Avalonia.Native/ScreenImpl.cs
+++ b/src/Avalonia.Native/ScreenImpl.cs
@@ -33,7 +33,7 @@ namespace Avalonia.Native
screen.PixelDensity,
screen.Bounds.ToAvaloniaPixelRect(),
screen.WorkingArea.ToAvaloniaPixelRect(),
- screen.Primary);
+ screen.Primary.FromComBool());
}
return result;
diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs
index 0239fc680d..8012813644 100644
--- a/src/Avalonia.Native/SystemDialogs.cs
+++ b/src/Avalonia.Native/SystemDialogs.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Native
if (dialog is OpenFileDialog ofd)
{
_native.OpenFileDialog(nativeParent,
- events, ofd.AllowMultiple,
+ events, ofd.AllowMultiple.AsComBool(),
ofd.Title ?? "",
ofd.Directory ?? "",
ofd.InitialFileName ?? "",
diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs
index b42831854d..f3b60f07be 100644
--- a/src/Avalonia.Native/WindowImpl.cs
+++ b/src/Avalonia.Native/WindowImpl.cs
@@ -1,4 +1,5 @@
using System;
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
@@ -17,6 +18,8 @@ namespace Avalonia.Native
private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
IAvnWindow _native;
private double _extendTitleBarHeight = -1;
+ private DoubleClickHelper _doubleClickHelper;
+
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
@@ -24,6 +27,8 @@ namespace Avalonia.Native
_factory = factory;
_opts = opts;
_glFeature = glFeature;
+ _doubleClickHelper = new DoubleClickHelper();
+
using (var e = new WindowEvents(this))
{
var context = _opts.UseGpu ? glFeature?.MainContext : null;
@@ -42,14 +47,14 @@ namespace Avalonia.Native
_parent = parent;
}
- bool IAvnWindowEvents.Closing()
+ int IAvnWindowEvents.Closing()
{
if (_parent.Closing != null)
{
- return _parent.Closing();
+ return _parent.Closing().AsComBool();
}
- return true;
+ return true.AsComBool();
}
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
@@ -69,7 +74,7 @@ namespace Avalonia.Native
public void CanResize(bool value)
{
- _native.SetCanResize(value);
+ _native.SetCanResize(value.AsComBool());
}
public void SetSystemDecorations(Controls.SystemDecorations enabled)
@@ -118,7 +123,22 @@ namespace Avalonia.Native
if(visual == null)
{
- _native.BeginMoveDrag();
+ if (_doubleClickHelper.IsDoubleClick(e.Timestamp, e.Position))
+ {
+ // TOGGLE WINDOW STATE.
+ if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen)
+ {
+ WindowState = WindowState.Normal;
+ }
+ else
+ {
+ WindowState = WindowState.Maximized;
+ }
+ }
+ else
+ {
+ _native.BeginMoveDrag();
+ }
}
}
}
@@ -145,7 +165,7 @@ namespace Avalonia.Native
{
_isExtended = extendIntoClientAreaHint;
- _native.SetExtendClientArea(extendIntoClientAreaHint);
+ _native.SetExtendClientArea(extendIntoClientAreaHint.AsComBool());
InvalidateExtendedMargins();
}
@@ -198,7 +218,7 @@ namespace Avalonia.Native
public void SetEnabled(bool enable)
{
- _native.SetEnabled(enable);
+ _native.SetEnabled(enable.AsComBool());
}
}
}
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 20b6b8ecc5..88c3144884 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -174,7 +174,7 @@ namespace Avalonia.Native
void IAvnWindowBaseEvents.Resized(AvnSize* size)
{
- if (_parent._native != null)
+ if (_parent?._native != null)
{
var s = new Size(size->Width, size->Height);
_parent._savedLogicalSize = s;
@@ -192,14 +192,14 @@ namespace Avalonia.Native
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
}
- bool IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
+ int IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
{
- return _parent.RawKeyEvent(type, timeStamp, modifiers, key);
+ return _parent.RawKeyEvent(type, timeStamp, modifiers, key).AsComBool();
}
- bool IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
+ int IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
{
- return _parent.RawTextInputEvent(timeStamp, text);
+ return _parent.RawTextInputEvent(timeStamp, text).AsComBool();
}
@@ -388,7 +388,7 @@ namespace Avalonia.Native
public void SetTopmost(bool value)
{
- _native.SetTopMost(value);
+ _native.SetTopMost(value.AsComBool());
}
public double RenderScaling => _native?.Scaling ?? 1;
@@ -456,7 +456,7 @@ namespace Avalonia.Native
TransparencyLevel = transparencyLevel;
- _native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
+ _native?.SetBlurEnabled((TransparencyLevel >= WindowTransparencyLevel.Blur).AsComBool());
TransparencyLevelChanged?.Invoke(TransparencyLevel);
}
}
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 1d36cce20d..3f33476c3d 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -1,5 +1,6 @@
@clr-namespace Avalonia.Native.Interop
@clr-access internal
+@clr-map bool int
@cpp-preamble @@
#include "com.h"
#include "key.h"
diff --git a/src/Avalonia.Styling/ApiCompatBaseline.txt b/src/Avalonia.Styling/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..001e165830
--- /dev/null
+++ b/src/Avalonia.Styling/ApiCompatBaseline.txt
@@ -0,0 +1,3 @@
+Compat issues with assembly Avalonia.Styling:
+MembersMustExist : Member 'public System.IObservable Avalonia.Controls.ResourceNodeExtensions.GetResourceObservable(Avalonia.IStyledElement, System.Object, System.Func)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 1
diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs
index 250274c39b..be1069a4da 100644
--- a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs
+++ b/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs
@@ -60,7 +60,7 @@ namespace Avalonia.Controls
}
public static IObservable