diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 6b910fc615..4e34d4b132 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -4,7 +4,6 @@ about: Create a report to help us improve Avalonia
title: ''
labels: bug
assignees: ''
-
---
**Describe the bug**
@@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
+
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
@@ -24,8 +24,9 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- - OS: [e.g. Windows, Mac, Linux (State distribution)]
- - Version [e.g. 0.10.0-rc1 or 0.9.12]
+
+- OS: [e.g. Windows, Mac, Linux (State distribution)]
+- Version [e.g. 0.10.0-rc1 or 0.9.12]
**Additional context**
Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..687355d825
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Questions, Discussions, Ideas
+ url: https://github.com/AvaloniaUI/Avalonia/discussions/new
+ about: Please ask and answer questions here.
+ - name: Avalonia Community Support on Gitter
+ url: https://gitter.im/AvaloniaUI/Avalonia
+ about: Please ask and answer questions here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 11fc491ef1..5f0a04cee3 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -4,7 +4,6 @@ about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
-
---
**Is your feature request related to a problem? Please describe.**
diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm
index b6f9ed5071..1732d6e71f 100644
--- a/native/Avalonia.Native/src/OSX/cursor.mm
+++ b/native/Avalonia.Native/src/OSX/cursor.mm
@@ -62,6 +62,28 @@ public:
return S_OK;
}
+
+ virtual HRESULT CreateCustomCursor (void* bitmapData, size_t length, AvnPixelSize hotPixel, IAvnCursor** retOut) override
+ {
+ if(bitmapData == nullptr || retOut == nullptr)
+ {
+ return E_POINTER;
+ }
+
+ NSData *imageData = [NSData dataWithBytes:bitmapData length:length];
+ NSImage *image = [[NSImage alloc] initWithData:imageData];
+
+
+ NSPoint hotSpot;
+ hotSpot.x = hotPixel.Width;
+ hotSpot.y = hotPixel.Height;
+
+ *retOut = new Cursor([[NSCursor new] initWithImage: image hotSpot: hotSpot]);
+
+ (*retOut)->AddRef();
+
+ return S_OK;
+ }
};
extern IAvnCursorFactory* CreateCursorFactory()
diff --git a/samples/ControlCatalog/Assets/avalonia-32.png b/samples/ControlCatalog/Assets/avalonia-32.png
new file mode 100644
index 0000000000..7b443e7a25
Binary files /dev/null and b/samples/ControlCatalog/Assets/avalonia-32.png differ
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index f001425964..142c532d75 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -22,6 +22,10 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml b/samples/ControlCatalog/Pages/CursorPage.xaml
new file mode 100644
index 0000000000..a28039ea3f
--- /dev/null
+++ b/samples/ControlCatalog/Pages/CursorPage.xaml
@@ -0,0 +1,29 @@
+
+
+
+ Cursor
+ Defines a cursor (mouse pointer)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml.cs b/samples/ControlCatalog/Pages/CursorPage.xaml.cs
new file mode 100644
index 0000000000..9e9e9ba8b9
--- /dev/null
+++ b/samples/ControlCatalog/Pages/CursorPage.xaml.cs
@@ -0,0 +1,20 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using ControlCatalog.ViewModels;
+
+namespace ControlCatalog.Pages
+{
+ public class CursorPage : UserControl
+ {
+ public CursorPage()
+ {
+ this.InitializeComponent();
+ DataContext = new CursorPageViewModel();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
new file mode 100644
index 0000000000..f1cc0637dc
--- /dev/null
+++ b/samples/ControlCatalog/ViewModels/CursorPageViewModel.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia;
+using Avalonia.Input;
+using Avalonia.Media.Imaging;
+using Avalonia.Platform;
+using MiniMvvm;
+
+namespace ControlCatalog.ViewModels
+{
+ public class CursorPageViewModel : ViewModelBase
+ {
+ public CursorPageViewModel()
+ {
+ StandardCursors = Enum.GetValues(typeof(StandardCursorType))
+ .Cast()
+ .Select(x => new StandardCursorModel(x))
+ .ToList();
+
+ var loader = AvaloniaLocator.Current.GetService();
+ var s = loader.Open(new Uri("avares://ControlCatalog/Assets/avalonia-32.png"));
+ var bitmap = new Bitmap(s);
+ CustomCursor = new Cursor(bitmap, new PixelPoint(16, 16));
+ }
+
+ public IEnumerable StandardCursors { get; }
+
+ public Cursor CustomCursor { get; }
+
+ public class StandardCursorModel
+ {
+ public StandardCursorModel(StandardCursorType type)
+ {
+ Type = type;
+ Cursor = new Cursor(type);
+ }
+
+ public StandardCursorType Type { get; }
+
+ public Cursor Cursor { get; }
+ }
+ }
+}
diff --git a/src/Avalonia.Base/EnumExtensions.cs b/src/Avalonia.Base/EnumExtensions.cs
index 1e4864283f..bc1f8d36a9 100644
--- a/src/Avalonia.Base/EnumExtensions.cs
+++ b/src/Avalonia.Base/EnumExtensions.cs
@@ -11,10 +11,32 @@ namespace Avalonia
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool HasFlagCustom(this T value, T flag) where T : unmanaged, Enum
{
- var intValue = *(int*)&value;
- var intFlag = *(int*)&flag;
-
- return (intValue & intFlag) == intFlag;
+ if (sizeof(T) == 1)
+ {
+ var byteValue = Unsafe.As(ref value);
+ var byteFlag = Unsafe.As(ref flag);
+ return (byteValue & byteFlag) == byteFlag;
+ }
+ else if (sizeof(T) == 2)
+ {
+ var shortValue = Unsafe.As(ref value);
+ var shortFlag = Unsafe.As(ref flag);
+ return (shortValue & shortFlag) == shortFlag;
+ }
+ else if (sizeof(T) == 4)
+ {
+ var intValue = Unsafe.As(ref value);
+ var intFlag = Unsafe.As(ref flag);
+ return (intValue & intFlag) == intFlag;
+ }
+ else if (sizeof(T) == 8)
+ {
+ var longValue = Unsafe.As(ref value);
+ var longFlag = Unsafe.As(ref flag);
+ return (longValue & longFlag) == longFlag;
+ }
+ else
+ throw new NotSupportedException("Enum with size of " + Unsafe.SizeOf() + " are not supported");
}
}
}
diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs
index d0d88166a7..097731bc60 100644
--- a/src/Avalonia.Base/Utilities/TypeUtilities.cs
+++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs
@@ -372,8 +372,8 @@ namespace Avalonia.Utilities
const string implicitName = "op_Implicit";
const string explicitName = "op_Explicit";
- bool allowImplicit = (operatorType & OperatorType.Implicit) != 0;
- bool allowExplicit = (operatorType & OperatorType.Explicit) != 0;
+ bool allowImplicit = operatorType.HasFlagCustom(OperatorType.Implicit);
+ bool allowExplicit = operatorType.HasFlagCustom(OperatorType.Explicit);
foreach (MethodInfo method in fromType.GetMethods())
{
diff --git a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
index 92734b128d..b97f2a2bcb 100644
--- a/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
+++ b/src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
@@ -2595,7 +2595,7 @@ namespace Avalonia.Collections
/// Whether the specified flag is set
private bool CheckFlag(CollectionViewFlags flags)
{
- return (_flags & flags) != 0;
+ return _flags.HasFlagCustom(flags);
}
///
diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..e5adc8c6ed
--- /dev/null
+++ b/src/Avalonia.Controls/ApiCompatBaseline.txt
@@ -0,0 +1,6 @@
+Compat issues with assembly Avalonia.Controls:
+MembersMustExist : Member 'public void Avalonia.Controls.Embedding.Offscreen.OffscreenTopLevelImplBase.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.ICursorImpl)' is present in the implementation but not in the contract.
+InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' is present in the contract but not in the implementation.
+MembersMustExist : Member 'public void Avalonia.Platform.ITopLevelImpl.SetCursor(Avalonia.Platform.IPlatformHandle)' does not exist in the implementation but it does exist in the contract.
+Total Issues: 4
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index 7f2acb58fe..20ca41bc57 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -188,7 +188,7 @@ namespace Avalonia.Controls
return;
if (e.Key == Key.F4 ||
- ((e.Key == Key.Down || e.Key == Key.Up) && ((e.KeyModifiers & KeyModifiers.Alt) != 0)))
+ ((e.Key == Key.Down || e.Key == Key.Up) && e.KeyModifiers.HasFlagCustom(KeyModifiers.Alt)))
{
IsDropDownOpen = !IsDropDownOpen;
e.Handled = true;
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index 522103c7bd..ca0e9d48b8 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -61,7 +61,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
- public virtual void SetCursor(IPlatformHandle cursor)
+ public virtual void SetCursor(ICursorImpl cursor)
{
}
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 6357ec98a8..66266c3b61 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/src/Avalonia.Controls/Grid.cs
@@ -637,7 +637,7 @@ namespace Avalonia.Controls
///
internal bool MeasureOverrideInProgress
{
- get { return (CheckFlagsAnd(Flags.MeasureOverrideInProgress)); }
+ get { return CheckFlags(Flags.MeasureOverrideInProgress); }
set { SetFlags(value, Flags.MeasureOverrideInProgress); }
}
@@ -646,7 +646,7 @@ namespace Avalonia.Controls
///
internal bool ArrangeOverrideInProgress
{
- get { return (CheckFlagsAnd(Flags.ArrangeOverrideInProgress)); }
+ get { return CheckFlags(Flags.ArrangeOverrideInProgress); }
set { SetFlags(value, Flags.ArrangeOverrideInProgress); }
}
@@ -2350,25 +2350,12 @@ namespace Avalonia.Controls
}
///
- /// CheckFlagsAnd returns true if all the flags in the
+ /// CheckFlags returns true if all the flags in the
/// given bitmask are set on the object.
///
- private bool CheckFlagsAnd(Flags flags)
+ private bool CheckFlags(Flags flags)
{
- return ((_flags & flags) == flags);
- }
-
- ///
- /// CheckFlagsOr returns true if at least one flag in the
- /// given bitmask is set.
- ///
- ///
- /// If no bits are set in the given bitmask, the method returns
- /// true.
- ///
- private bool CheckFlagsOr(Flags flags)
- {
- return (flags == 0 || (_flags & flags) != 0);
+ return _flags.HasFlagCustom(flags);
}
private static void OnShowGridLinesPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
@@ -2535,7 +2522,7 @@ namespace Avalonia.Controls
///
private bool CellsStructureDirty
{
- get { return (!CheckFlagsAnd(Flags.ValidCellsStructure)); }
+ get { return !CheckFlags(Flags.ValidCellsStructure); }
set { SetFlags(!value, Flags.ValidCellsStructure); }
}
@@ -2544,7 +2531,7 @@ namespace Avalonia.Controls
///
private bool ListenToNotifications
{
- get { return (CheckFlagsAnd(Flags.ListenToNotifications)); }
+ get { return CheckFlags(Flags.ListenToNotifications); }
set { SetFlags(value, Flags.ListenToNotifications); }
}
@@ -2553,7 +2540,7 @@ namespace Avalonia.Controls
///
private bool SizeToContentU
{
- get { return (CheckFlagsAnd(Flags.SizeToContentU)); }
+ get { return CheckFlags(Flags.SizeToContentU); }
set { SetFlags(value, Flags.SizeToContentU); }
}
@@ -2562,7 +2549,7 @@ namespace Avalonia.Controls
///
private bool SizeToContentV
{
- get { return (CheckFlagsAnd(Flags.SizeToContentV)); }
+ get { return CheckFlags(Flags.SizeToContentV); }
set { SetFlags(value, Flags.SizeToContentV); }
}
@@ -2571,7 +2558,7 @@ namespace Avalonia.Controls
///
private bool HasStarCellsU
{
- get { return (CheckFlagsAnd(Flags.HasStarCellsU)); }
+ get { return CheckFlags(Flags.HasStarCellsU); }
set { SetFlags(value, Flags.HasStarCellsU); }
}
@@ -2580,7 +2567,7 @@ namespace Avalonia.Controls
///
private bool HasStarCellsV
{
- get { return (CheckFlagsAnd(Flags.HasStarCellsV)); }
+ get { return CheckFlags(Flags.HasStarCellsV); }
set { SetFlags(value, Flags.HasStarCellsV); }
}
@@ -2589,7 +2576,7 @@ namespace Avalonia.Controls
///
private bool HasGroup3CellsInAutoRows
{
- get { return (CheckFlagsAnd(Flags.HasGroup3CellsInAutoRows)); }
+ get { return CheckFlags(Flags.HasGroup3CellsInAutoRows); }
set { SetFlags(value, Flags.HasGroup3CellsInAutoRows); }
}
@@ -2803,10 +2790,10 @@ namespace Avalonia.Controls
internal LayoutTimeSizeType SizeTypeU;
internal LayoutTimeSizeType SizeTypeV;
internal int Next;
- internal bool IsStarU { get { return ((SizeTypeU & LayoutTimeSizeType.Star) != 0); } }
- internal bool IsAutoU { get { return ((SizeTypeU & LayoutTimeSizeType.Auto) != 0); } }
- internal bool IsStarV { get { return ((SizeTypeV & LayoutTimeSizeType.Star) != 0); } }
- internal bool IsAutoV { get { return ((SizeTypeV & LayoutTimeSizeType.Auto) != 0); } }
+ internal bool IsStarU => SizeTypeU.HasFlagCustom(LayoutTimeSizeType.Star);
+ internal bool IsAutoU => SizeTypeU.HasFlagCustom(LayoutTimeSizeType.Auto);
+ internal bool IsStarV => SizeTypeV.HasFlagCustom(LayoutTimeSizeType.Star);
+ internal bool IsAutoV => SizeTypeV.HasFlagCustom(LayoutTimeSizeType.Auto);
}
///
diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs
index d1b8038581..b6b3cc786c 100644
--- a/src/Avalonia.Controls/ListBox.cs
+++ b/src/Avalonia.Controls/ListBox.cs
@@ -135,8 +135,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
- (e.KeyModifiers & KeyModifiers.Shift) != 0,
- (e.KeyModifiers & KeyModifiers.Control) != 0);
+ e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift),
+ e.KeyModifiers.HasFlagCustom(KeyModifiers.Control));
}
}
@@ -154,8 +154,8 @@ namespace Avalonia.Controls
e.Handled = UpdateSelectionFromEventSource(
e.Source,
true,
- (e.KeyModifiers & KeyModifiers.Shift) != 0,
- (e.KeyModifiers & KeyModifiers.Control) != 0,
+ e.KeyModifiers.HasFlagCustom(KeyModifiers.Shift),
+ e.KeyModifiers.HasFlagCustom(KeyModifiers.Control),
point.Properties.IsRightButtonPressed);
}
}
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index 71ac0fa523..94099a970e 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -103,6 +103,7 @@ namespace Avalonia.Controls
private bool _commandCanExecute = true;
private Popup? _popup;
private KeyGesture _hotkey;
+ private bool _isEmbeddedInMenu;
///
/// Initializes static members of the class.
@@ -112,6 +113,7 @@ namespace Avalonia.Controls
SelectableMixin.Attach