Browse Source

Merge branch 'master' into alloc-boxing

pull/3066/head
Jumar Macato 7 years ago
committed by GitHub
parent
commit
7b60dce085
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Avalonia.sln.DotSettings
  2. 1
      build/Base.props
  3. 1
      build/BuildTargets.targets
  4. 2
      global.json
  5. 31
      native/Avalonia.Native/inc/avalonia-native.h
  6. 9
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  7. 8
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  8. 12
      native/Avalonia.Native/src/OSX/common.h
  9. 151
      native/Avalonia.Native/src/OSX/main.mm
  10. 80
      native/Avalonia.Native/src/OSX/menu.h
  11. 307
      native/Avalonia.Native/src/OSX/menu.mm
  12. 6
      native/Avalonia.Native/src/OSX/platformthreading.mm
  13. 2
      native/Avalonia.Native/src/OSX/window.h
  14. 113
      native/Avalonia.Native/src/OSX/window.mm
  15. 2
      packages/Avalonia/AvaloniaBuildTasks.targets
  16. 8
      readme.md
  17. 2
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  18. 2
      samples/ControlCatalog.Android/MainActivity.cs
  19. 6
      samples/ControlCatalog.NetCore/Program.cs
  20. 18
      samples/ControlCatalog/App.xaml
  21. 13
      samples/ControlCatalog/App.xaml.cs
  22. 25
      samples/ControlCatalog/DecoratedWindow.xaml
  23. 8
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  24. 31
      samples/ControlCatalog/MainWindow.xaml
  25. 14
      samples/ControlCatalog/MainWindow.xaml.cs
  26. 5
      samples/ControlCatalog/Pages/MenuPage.xaml
  27. 6
      src/Android/Avalonia.Android/AndroidPlatform.cs
  28. 2
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  29. 2
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  30. 23
      src/Avalonia.Base/AvaloniaObject.cs
  31. 9
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  32. 2
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  33. 71
      src/Avalonia.Base/Logging/ILogSink.cs
  34. 125
      src/Avalonia.Base/Logging/Logger.cs
  35. 174
      src/Avalonia.Base/Logging/ParametrizedLogger.cs
  36. 2
      src/Avalonia.Base/PriorityValue.cs
  37. 4
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  38. 3
      src/Avalonia.Build.Tasks/Program.cs
  39. 4
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  40. 50
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  41. 4
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  42. 4
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  43. 8
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  44. 4
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  45. 50
      src/Avalonia.Controls/AppBuilderBase.cs
  46. 19
      src/Avalonia.Controls/Application.cs
  47. 3
      src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
  48. 18
      src/Avalonia.Controls/AutoCompleteBox.cs
  49. 2
      src/Avalonia.Controls/Button.cs
  50. 25
      src/Avalonia.Controls/Calendar/Calendar.cs
  51. 2
      src/Avalonia.Controls/Calendar/CalendarButton.cs
  52. 2
      src/Avalonia.Controls/Calendar/CalendarDayButton.cs
  53. 16
      src/Avalonia.Controls/Calendar/DatePicker.cs
  54. 12
      src/Avalonia.Controls/Carousel.cs
  55. 4
      src/Avalonia.Controls/ComboBox.cs
  56. 2
      src/Avalonia.Controls/ContentControl.cs
  57. 2
      src/Avalonia.Controls/ContextMenu.cs
  58. 2
      src/Avalonia.Controls/DataValidationErrors.cs
  59. 2
      src/Avalonia.Controls/Decorator.cs
  60. 4
      src/Avalonia.Controls/DropDown.cs
  61. 2
      src/Avalonia.Controls/Expander.cs
  62. 11
      src/Avalonia.Controls/Grid.cs
  63. 12
      src/Avalonia.Controls/ItemsControl.cs
  64. 8
      src/Avalonia.Controls/LayoutTransformControl.cs
  65. 2
      src/Avalonia.Controls/MenuBase.cs
  66. 16
      src/Avalonia.Controls/MenuItem.cs
  67. 85
      src/Avalonia.Controls/NativeMenu.Export.cs
  68. 60
      src/Avalonia.Controls/NativeMenu.cs
  69. 36
      src/Avalonia.Controls/NativeMenuBar.cs
  70. 162
      src/Avalonia.Controls/NativeMenuItem.cs
  71. 23
      src/Avalonia.Controls/NativeMenuItemBase.cs
  72. 10
      src/Avalonia.Controls/NativeMenuItemSeperator.cs
  73. 5
      src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs
  74. 18
      src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs
  75. 13
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  76. 6
      src/Avalonia.Controls/Platform/InProcessDragSource.cs
  77. 4
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  78. 6
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  79. 2
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  80. 2
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  81. 6
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  82. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  83. 2
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  84. 2
      src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
  85. 2
      src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
  86. 4
      src/Avalonia.Controls/Primitives/Popup.cs
  87. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  88. 4
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  89. 8
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  90. 4
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  91. 10
      src/Avalonia.Controls/Primitives/Thumb.cs
  92. 6
      src/Avalonia.Controls/Primitives/Track.cs
  93. 5
      src/Avalonia.Controls/Primitives/UniformGrid.cs
  94. 5
      src/Avalonia.Controls/ProgressBar.cs
  95. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  96. 4
      src/Avalonia.Controls/RepeatButton.cs
  97. 4
      src/Avalonia.Controls/ScrollViewer.cs
  98. 6
      src/Avalonia.Controls/Slider.cs
  99. 2
      src/Avalonia.Controls/TabControl.cs
  100. 4
      src/Avalonia.Controls/TabItem.cs

1
Avalonia.sln.DotSettings

@ -3,6 +3,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=3E53A01A_002DB331_002D47F3_002DB828_002D4A5717E77A24_002Fd_003Aglass/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=6417B24E_002D49C2_002D4985_002D8DB2_002D3AB9D898EC91/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=E3A1060B_002D50D0_002D44E8_002D88B6_002DF44EF2E5BD72_002Ff_003Ahtml_002Ehtm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=MemberCanBePrivate_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String> <s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantUsingDirective/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=DECLSPEC_005FPROPERTY/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=DECLSPEC_005FPROPERTY/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=ENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CppNaming/UserRules/=ENUM/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /&gt;</s:String>

1
build/Base.props

@ -1,5 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

1
build/BuildTargets.targets

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation> <AvaloniaBuildTasksLocation>$(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll</AvaloniaBuildTasksLocation>
<AvaloniaUseExternalMSBuild>true</AvaloniaUseExternalMSBuild> <AvaloniaUseExternalMSBuild>true</AvaloniaUseExternalMSBuild>
<AvaloniaXamlIlVerifyIl>true</AvaloniaXamlIlVerifyIl>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.props"/> <Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.props"/>
<Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.targets"/> <Import Project="$(MSBuildThisFileDirectory)\..\packages\Avalonia\AvaloniaBuildTasks.targets"/>

2
global.json

@ -1,7 +1,7 @@
{ {
"msbuild-sdks": { "msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43", "Microsoft.Build.Traversal": "1.0.43",
"MSBuild.Sdk.Extras": "1.6.65", "MSBuild.Sdk.Extras": "2.0.46",
"AggregatePackage.NuGet.Sdk" : "0.1.12" "AggregatePackage.NuGet.Sdk" : "0.1.12"
} }
} }

31
native/Avalonia.Native/inc/avalonia-native.h

@ -22,6 +22,8 @@ struct IAvnGlContext;
struct IAvnGlDisplay; struct IAvnGlDisplay;
struct IAvnGlSurfaceRenderTarget; struct IAvnGlSurfaceRenderTarget;
struct IAvnGlSurfaceRenderingSession; struct IAvnGlSurfaceRenderingSession;
struct IAvnAppMenu;
struct IAvnAppMenuItem;
struct AvnSize struct AvnSize
{ {
@ -173,6 +175,11 @@ public:
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0; virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0; virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0;
virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0; virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0;
virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) = 0;
virtual HRESULT SetAppMenu(IAvnAppMenu* menu) = 0;
virtual HRESULT CreateMenu (IAvnAppMenu** ppv) = 0;
virtual HRESULT CreateMenuItem (IAvnAppMenuItem** ppv) = 0;
virtual HRESULT CreateMenuItemSeperator (IAvnAppMenuItem** ppv) = 0;
}; };
AVNCOM(IAvnString, 17) : IUnknown AVNCOM(IAvnString, 17) : IUnknown
@ -203,6 +210,8 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT SetCursor(IAvnCursor* cursor) = 0; virtual HRESULT SetCursor(IAvnCursor* cursor) = 0;
virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0; virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0;
virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0; virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0;
virtual HRESULT SetMainMenu(IAvnAppMenu* menu) = 0;
virtual HRESULT ObtainMainMenu(IAvnAppMenu** retOut) = 0;
virtual bool TryLock() = 0; virtual bool TryLock() = 0;
virtual void Unlock() = 0; virtual void Unlock() = 0;
}; };
@ -258,6 +267,7 @@ AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
AVNCOM(IAvnMacOptions, 07) : IUnknown AVNCOM(IAvnMacOptions, 07) : IUnknown
{ {
virtual HRESULT SetShowInDock(int show) = 0; virtual HRESULT SetShowInDock(int show) = 0;
virtual HRESULT SetApplicationTitle (void* utf8string) = 0;
}; };
AVNCOM(IAvnActionCallback, 08) : IUnknown AVNCOM(IAvnActionCallback, 08) : IUnknown
@ -367,4 +377,25 @@ AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown
virtual HRESULT GetScaling(double* ret) = 0; virtual HRESULT GetScaling(double* ret) = 0;
}; };
AVNCOM(IAvnAppMenu, 17) : IUnknown
{
virtual HRESULT AddItem (IAvnAppMenuItem* item) = 0;
virtual HRESULT RemoveItem (IAvnAppMenuItem* item) = 0;
virtual HRESULT SetTitle (void* utf8String) = 0;
virtual HRESULT Clear () = 0;
};
AVNCOM(IAvnPredicateCallback, 18) : IUnknown
{
virtual bool Evaluate() = 0;
};
AVNCOM(IAvnAppMenuItem, 19) : IUnknown
{
virtual HRESULT SetSubMenu (IAvnAppMenu* menu) = 0;
virtual HRESULT SetTitle (void* utf8String) = 0;
virtual HRESULT SetGesture (void* utf8String, AvnInputModifiers modifiers) = 0;
virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) = 0;
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative(); extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();

9
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -8,10 +8,12 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; }; 37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; }; 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; }; 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.mm */; };
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; }; 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
520624B322973F4100C4DCEF /* menu.mm in Sources */ = {isa = PBXBuildFile; fileRef = 520624B222973F4100C4DCEF /* menu.mm */; };
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
@ -24,6 +26,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; }; 379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; }; 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; }; 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; };
@ -32,6 +35,7 @@
37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = "<group>"; }; 37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = "<group>"; };
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; }; 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = "<group>"; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = "<group>"; };
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; }; 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; }; 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
@ -85,6 +89,8 @@
AB661C1F2148286E00291242 /* window.mm */, AB661C1F2148286E00291242 /* window.mm */,
37C09D8A21581EF2006A6758 /* window.h */, 37C09D8A21581EF2006A6758 /* window.h */,
AB00E4F62147CA920032A60A /* main.mm */, AB00E4F62147CA920032A60A /* main.mm */,
37155CE3233C00EB0034DCE9 /* menu.h */,
520624B222973F4100C4DCEF /* menu.mm */,
37A517B22159597E00FBA241 /* Screens.mm */, 37A517B22159597E00FBA241 /* Screens.mm */,
37C09D8721580FE4006A6758 /* SystemDialogs.mm */, 37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
AB7A61F02147C815003C5833 /* Products */, AB7A61F02147C815003C5833 /* Products */,
@ -107,6 +113,7 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -150,6 +157,7 @@
developmentRegion = English; developmentRegion = English;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
English,
en, en,
); );
mainGroup = AB7A61E62147C814003C5833; mainGroup = AB7A61E62147C814003C5833;
@ -173,6 +181,7 @@
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */, 37DDA9B0219330F8002E132B /* AvnString.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */, AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,

8
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

12
native/Avalonia.Native/src/OSX/common.h

@ -19,6 +19,13 @@ extern IAvnClipboard* CreateClipboard();
extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnCursorFactory* CreateCursorFactory();
extern IAvnGlFeature* GetGlFeature(); extern IAvnGlFeature* GetGlFeature();
extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view); extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view);
extern IAvnAppMenu* CreateAppMenu();
extern IAvnAppMenuItem* CreateAppMenuItem();
extern IAvnAppMenuItem* CreateAppMenuItemSeperator();
extern void SetAppMenu (NSString* appName, IAvnAppMenu* appMenu);
extern IAvnAppMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void InitializeAvnApp(); extern void InitializeAvnApp();
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy; extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p); extern NSPoint ToNSPoint (AvnPoint p);
@ -40,4 +47,9 @@ template<typename T> inline T* objc_cast(id from) {
return nil; return nil;
} }
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@end
#endif #endif

151
native/Avalonia.Native/src/OSX/main.mm

@ -5,10 +5,121 @@
#define COM_GUIDS_MATERIALIZE #define COM_GUIDS_MATERIALIZE
#include "common.h" #include "common.h"
static NSString* s_appTitle = @"Avalonia";
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
void SetProcessName(NSString* appTitle) {
s_appTitle = appTitle;
CFStringRef process_name = (__bridge CFStringRef)appTitle;
if (!process_name || CFStringGetLength(process_name) == 0) {
//NOTREACHED() << "SetProcessName given bad name.";
return;
}
if (![NSThread isMainThread]) {
//NOTREACHED() << "Should only set process name from main thread.";
return;
}
// Warning: here be dragons! This is SPI reverse-engineered from WebKit's
// plugin host, and could break at any time (although realistically it's only
// likely to break in a new major release).
// When 10.7 is available, check that this still works, and update this
// comment for 10.8.
// Private CFType used in these LaunchServices calls.
typedef CFTypeRef PrivateLSASN;
typedef PrivateLSASN (*LSGetCurrentApplicationASNType)();
typedef OSStatus (*LSSetApplicationInformationItemType)(int, PrivateLSASN,
CFStringRef,
CFStringRef,
CFDictionaryRef*);
static LSGetCurrentApplicationASNType ls_get_current_application_asn_func =
NULL;
static LSSetApplicationInformationItemType
ls_set_application_information_item_func = NULL;
static CFStringRef ls_display_name_key = NULL;
static bool did_symbol_lookup = false;
if (!did_symbol_lookup) {
did_symbol_lookup = true;
CFBundleRef launch_services_bundle =
CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices"));
if (!launch_services_bundle) {
//LOG(ERROR) << "Failed to look up LaunchServices bundle";
return;
}
ls_get_current_application_asn_func =
reinterpret_cast<LSGetCurrentApplicationASNType>(
CFBundleGetFunctionPointerForName(
launch_services_bundle, CFSTR("_LSGetCurrentApplicationASN")));
if (!ls_get_current_application_asn_func){}
//LOG(ERROR) << "Could not find _LSGetCurrentApplicationASN";
ls_set_application_information_item_func =
reinterpret_cast<LSSetApplicationInformationItemType>(
CFBundleGetFunctionPointerForName(
launch_services_bundle,
CFSTR("_LSSetApplicationInformationItem")));
if (!ls_set_application_information_item_func){}
//LOG(ERROR) << "Could not find _LSSetApplicationInformationItem";
CFStringRef* key_pointer = reinterpret_cast<CFStringRef*>(
CFBundleGetDataPointerForName(launch_services_bundle,
CFSTR("_kLSDisplayNameKey")));
ls_display_name_key = key_pointer ? *key_pointer : NULL;
if (!ls_display_name_key){}
//LOG(ERROR) << "Could not find _kLSDisplayNameKey";
// Internally, this call relies on the Mach ports that are started up by the
// Carbon Process Manager. In debug builds this usually happens due to how
// the logging layers are started up; but in release, it isn't started in as
// much of a defined order. So if the symbols had to be loaded, go ahead
// and force a call to make sure the manager has been initialized and hence
// the ports are opened.
ProcessSerialNumber psn;
GetCurrentProcess(&psn);
}
if (!ls_get_current_application_asn_func ||
!ls_set_application_information_item_func ||
!ls_display_name_key) {
return;
}
PrivateLSASN asn = ls_get_current_application_asn_func();
// Constant used by WebKit; what exactly it means is unknown.
const int magic_session_constant = -2;
OSErr err =
ls_set_application_information_item_func(magic_session_constant, asn,
ls_display_name_key,
process_name,
NULL /* optional out param */);
//LOG_IF(ERROR, err) << "Call to set process name failed, err " << err;
}
class MacOptions : public ComSingleObject<IAvnMacOptions, &IID_IAvnMacOptions> class MacOptions : public ComSingleObject<IAvnMacOptions, &IID_IAvnMacOptions>
{ {
public: public:
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
virtual HRESULT SetApplicationTitle(void* utf8String) override
{
auto appTitle = [NSString stringWithUTF8String:(const char*)utf8String];
[[NSProcessInfo processInfo] setProcessName:appTitle];
SetProcessName(appTitle);
return S_OK;
}
virtual HRESULT SetShowInDock(int show) override virtual HRESULT SetShowInDock(int show) override
{ {
AvnDesiredActivationPolicy = show AvnDesiredActivationPolicy = show
@ -17,8 +128,6 @@ public:
} }
}; };
/// See "Using POSIX Threads in a Cocoa Application" section here: /// See "Using POSIX Threads in a Cocoa Application" section here:
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024 /// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
@interface ThreadingInitializer : NSObject @interface ThreadingInitializer : NSObject
@ -43,8 +152,6 @@ public:
close(_fds[0]); close(_fds[0]);
close(_fds[1]); close(_fds[1]);
} }
@end @end
@ -123,6 +230,42 @@ public:
*ppv = rv; *ppv = rv;
return S_OK; return S_OK;
} }
virtual HRESULT CreateMenu (IAvnAppMenu** ppv) override
{
*ppv = ::CreateAppMenu();
return S_OK;
}
virtual HRESULT CreateMenuItem (IAvnAppMenuItem** ppv) override
{
*ppv = ::CreateAppMenuItem();
return S_OK;
}
virtual HRESULT CreateMenuItemSeperator (IAvnAppMenuItem** ppv) override
{
*ppv = ::CreateAppMenuItemSeperator();
return S_OK;
}
virtual HRESULT SetAppMenu (IAvnAppMenu* appMenu) override
{
::SetAppMenu(s_appTitle, appMenu);
return S_OK;
}
virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) override
{
if(retOut == nullptr)
{
return E_POINTER;
}
*retOut = ::GetAppMenu();
return S_OK;
}
}; };
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()

80
native/Avalonia.Native/src/OSX/menu.h

@ -0,0 +1,80 @@
//
// menu.h
// Avalonia.Native.OSX
//
// Created by Dan Walmsley on 01/08/2019.
// Copyright © 2019 Avalonia. All rights reserved.
//
#ifndef menu_h
#define menu_h
#include "common.h"
class AvnAppMenuItem;
class AvnAppMenu;
@interface AvnMenu : NSMenu // for some reason it doesnt detect nsmenu here but compiler doesnt complain
- (void)setMenu:(NSMenu*) menu;
@end
@interface AvnMenuItem : NSMenuItem
- (id) initWithAvnAppMenuItem: (AvnAppMenuItem*)menuItem;
- (void)didSelectItem:(id)sender;
@end
class AvnAppMenuItem : public ComSingleObject<IAvnAppMenuItem, &IID_IAvnAppMenuItem>
{
private:
NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem
IAvnActionCallback* _callback;
IAvnPredicateCallback* _predicate;
bool _isSeperator;
public:
FORWARD_IUNKNOWN()
AvnAppMenuItem(bool isSeperator);
NSMenuItem* GetNative();
virtual HRESULT SetSubMenu (IAvnAppMenu* menu) override;
virtual HRESULT SetTitle (void* utf8String) override;
virtual HRESULT SetGesture (void* key, AvnInputModifiers modifiers) override;
virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override;
bool EvaluateItemEnabled();
void RaiseOnClicked();
};
class AvnAppMenu : public ComSingleObject<IAvnAppMenu, &IID_IAvnAppMenu>
{
private:
AvnMenu* _native;
public:
FORWARD_IUNKNOWN()
AvnAppMenu();
AvnAppMenu(AvnMenu* native);
AvnMenu* GetNative();
virtual HRESULT AddItem (IAvnAppMenuItem* item) override;
virtual HRESULT RemoveItem (IAvnAppMenuItem* item) override;
virtual HRESULT SetTitle (void* utf8String) override;
virtual HRESULT Clear () override;
};
#endif

307
native/Avalonia.Native/src/OSX/menu.mm

@ -0,0 +1,307 @@
#include "common.h"
#include "menu.h"
#include "window.h"
@implementation AvnMenu
@end
@implementation AvnMenuItem
{
AvnAppMenuItem* _item;
}
- (id) initWithAvnAppMenuItem: (AvnAppMenuItem*)menuItem
{
if(self != nil)
{
_item = menuItem;
self = [super initWithTitle:@""
action:@selector(didSelectItem:)
keyEquivalent:@""];
[self setEnabled:YES];
[self setTarget:self];
}
return self;
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
if([self submenu] != nil)
{
return YES;
}
return _item->EvaluateItemEnabled();
}
- (void)didSelectItem:(nullable id)sender
{
_item->RaiseOnClicked();
}
@end
AvnAppMenuItem::AvnAppMenuItem(bool isSeperator)
{
_isSeperator = isSeperator;
if(isSeperator)
{
_native = [NSMenuItem separatorItem];
}
else
{
_native = [[AvnMenuItem alloc] initWithAvnAppMenuItem: this];
}
_callback = nullptr;
}
NSMenuItem* AvnAppMenuItem::GetNative()
{
return _native;
}
HRESULT AvnAppMenuItem::SetSubMenu (IAvnAppMenu* menu)
{
auto nsMenu = dynamic_cast<AvnAppMenu*>(menu)->GetNative();
[_native setSubmenu: nsMenu];
return S_OK;
}
HRESULT AvnAppMenuItem::SetTitle (void* utf8String)
{
if (utf8String != nullptr)
{
[_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]];
}
return S_OK;
}
HRESULT AvnAppMenuItem::SetGesture (void* key, AvnInputModifiers modifiers)
{
NSEventModifierFlags flags = 0;
if (modifiers & Control)
flags |= NSEventModifierFlagControl;
if (modifiers & Shift)
flags |= NSEventModifierFlagShift;
if (modifiers & Alt)
flags |= NSEventModifierFlagOption;
if (modifiers & Windows)
flags |= NSEventModifierFlagCommand;
[_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]];
[_native setKeyEquivalentModifierMask:flags];
return S_OK;
}
HRESULT AvnAppMenuItem::SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback)
{
_predicate = predicate;
_callback = callback;
return S_OK;
}
bool AvnAppMenuItem::EvaluateItemEnabled()
{
if(_predicate != nullptr)
{
auto result = _predicate->Evaluate ();
return result;
}
return false;
}
void AvnAppMenuItem::RaiseOnClicked()
{
if(_callback != nullptr)
{
_callback->Run();
}
}
AvnAppMenu::AvnAppMenu()
{
_native = [AvnMenu new];
}
AvnAppMenu::AvnAppMenu(AvnMenu* native)
{
_native = native;
}
AvnMenu* AvnAppMenu::GetNative()
{
return _native;
}
HRESULT AvnAppMenu::AddItem (IAvnAppMenuItem* item)
{
auto avnMenuItem = dynamic_cast<AvnAppMenuItem*>(item);
if(avnMenuItem != nullptr)
{
[_native addItem: avnMenuItem->GetNative()];
}
return S_OK;
}
HRESULT AvnAppMenu::RemoveItem (IAvnAppMenuItem* item)
{
auto avnMenuItem = dynamic_cast<AvnAppMenuItem*>(item);
if(avnMenuItem != nullptr)
{
[_native removeItem:avnMenuItem->GetNative()];
}
return S_OK;
}
HRESULT AvnAppMenu::SetTitle (void* utf8String)
{
if (utf8String != nullptr)
{
[_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]];
}
return S_OK;
}
HRESULT AvnAppMenu::Clear()
{
[_native removeAllItems];
return S_OK;
}
extern IAvnAppMenu* CreateAppMenu()
{
@autoreleasepool
{
id menuBar = [NSMenu new];
return new AvnAppMenu(menuBar);
}
}
extern IAvnAppMenuItem* CreateAppMenuItem()
{
@autoreleasepool
{
return new AvnAppMenuItem(false);
}
}
extern IAvnAppMenuItem* CreateAppMenuItemSeperator()
{
@autoreleasepool
{
return new AvnAppMenuItem(true);
}
}
static IAvnAppMenu* s_appMenu = nullptr;
static NSMenuItem* s_appMenuItem = nullptr;
extern void SetAppMenu (NSString* appName, IAvnAppMenu* menu)
{
s_appMenu = menu;
if(s_appMenu != nullptr)
{
auto nativeMenu = dynamic_cast<AvnAppMenu*>(s_appMenu);
auto currentMenu = [s_appMenuItem menu];
if (currentMenu != nullptr)
{
[currentMenu removeItem:s_appMenuItem];
}
s_appMenuItem = [nativeMenu->GetNative() itemAtIndex:0];
if (currentMenu == nullptr)
{
currentMenu = [s_appMenuItem menu];
}
[[s_appMenuItem menu] removeItem:s_appMenuItem];
[currentMenu insertItem:s_appMenuItem atIndex:0];
if([s_appMenuItem submenu] == nullptr)
{
[s_appMenuItem setSubmenu:[NSMenu new]];
}
auto appMenu = [s_appMenuItem submenu];
[appMenu addItem:[NSMenuItem separatorItem]];
// Services item and menu
auto servicesItem = [[NSMenuItem alloc] init];
servicesItem.title = @"Services";
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"];
servicesItem.submenu = servicesMenu;
[NSApplication sharedApplication].servicesMenu = servicesMenu;
[appMenu addItem:servicesItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Hide Application
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"];
[appMenu addItem:hideItem];
// Hide Others
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"
action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption;
[appMenu addItem:hideAllOthersItem];
// Show All
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All"
action:@selector(unhideAllApplications:)
keyEquivalent:@""];
[appMenu addItem:showAllItem];
[appMenu addItem:[NSMenuItem separatorItem]];
// Quit Application
auto quitItem = [[NSMenuItem alloc] init];
quitItem.title = [@"Quit " stringByAppendingString:appName];
quitItem.keyEquivalent = @"q";
quitItem.target = [AvnWindow class];
quitItem.action = @selector(closeAll);
[appMenu addItem:quitItem];
}
else
{
s_appMenuItem = nullptr;
}
}
extern IAvnAppMenu* GetAppMenu ()
{
return s_appMenu;
}
extern NSMenuItem* GetAppMenuItem ()
{
return s_appMenuItem;
}

6
native/Avalonia.Native/src/OSX/platformthreading.mm

@ -10,12 +10,6 @@ class PlatformThreadingInterface;
-(Signaler*) init; -(Signaler*) init;
@end @end
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@end
@implementation ActionCallback @implementation ActionCallback
{ {
ComPtr<IAvnActionCallback> _callback; ComPtr<IAvnActionCallback> _callback;

2
native/Avalonia.Native/src/OSX/window.h

@ -15,11 +15,13 @@ class WindowBaseImpl;
@end @end
@interface AvnWindow : NSWindow <NSWindowDelegate> @interface AvnWindow : NSWindow <NSWindowDelegate>
+(void) closeAll;
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(void) setCanBecomeKeyAndMain; -(void) setCanBecomeKeyAndMain;
-(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow; -(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents; -(bool) shouldTryToHandleEvents;
-(void) applyMenu:(NSMenu *)menu;
@end @end
struct INSWindowHolder struct INSWindowHolder

113
native/Avalonia.Native/src/OSX/window.mm

@ -5,6 +5,7 @@
#include "window.h" #include "window.h"
#include "KeyTransform.h" #include "KeyTransform.h"
#include "cursor.h" #include "cursor.h"
#include "menu.h"
#include <OpenGL/gl.h> #include <OpenGL/gl.h>
class SoftwareDrawingOperation class SoftwareDrawingOperation
@ -63,9 +64,11 @@ public:
SoftwareDrawingOperation CurrentSwDrawingOperation; SoftwareDrawingOperation CurrentSwDrawingOperation;
AvnPoint lastPositionSet; AvnPoint lastPositionSet;
NSString* _lastTitle; NSString* _lastTitle;
IAvnAppMenu* _mainMenu;
WindowBaseImpl(IAvnWindowBaseEvents* events) WindowBaseImpl(IAvnWindowBaseEvents* events)
{ {
_mainMenu = nullptr;
BaseEvents = events; BaseEvents = events;
View = [[AvnView alloc] initWithParent:this]; View = [[AvnView alloc] initWithParent:this];
@ -93,6 +96,7 @@ public:
UpdateStyle(); UpdateStyle();
[Window makeKeyAndOrderFront:Window]; [Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
[Window setTitle:_lastTitle]; [Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible]; [Window setTitleVisibility:NSWindowTitleVisible];
@ -122,6 +126,7 @@ public:
if(Window != nullptr) if(Window != nullptr)
{ {
[Window makeKeyWindow]; [Window makeKeyWindow];
[NSApp activateIgnoringOtherApps:YES];
} }
} }
@ -209,6 +214,31 @@ public:
} }
} }
virtual HRESULT SetMainMenu(IAvnAppMenu* menu) override
{
_mainMenu = menu;
auto nativeMenu = dynamic_cast<AvnAppMenu*>(menu);
auto nsmenu = nativeMenu->GetNative();
[Window applyMenu:nsmenu];
return S_OK;
}
virtual HRESULT ObtainMainMenu(IAvnAppMenu** ret) override
{
if(ret == nullptr)
{
return E_POINTER;
}
*ret = _mainMenu;
return S_OK;
}
virtual bool TryLock() override virtual bool TryLock() override
{ {
@autoreleasepool @autoreleasepool
@ -825,8 +855,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
if(type == Wheel) if(type == Wheel)
{ {
delta.X = [event scrollingDeltaX] / 5; delta.X = [event scrollingDeltaX] / 50;
delta.Y = [event scrollingDeltaY] / 5; delta.Y = [event scrollingDeltaY] / 50;
if(delta.X == 0 && delta.Y == 0) if(delta.X == 0 && delta.Y == 0)
{ {
@ -1042,6 +1072,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
ComPtr<WindowBaseImpl> _parent; ComPtr<WindowBaseImpl> _parent;
bool _canBecomeKeyAndMain; bool _canBecomeKeyAndMain;
bool _closed; bool _closed;
NSMenu* _menu;
bool _isAppMenuApplied;
}
+(void)closeAll
{
NSArray<NSWindow*>* windows = [NSArray arrayWithArray:[NSApp windows]];
auto numWindows = [windows count];
for(int i = 0; i < numWindows; i++)
{
[[windows objectAtIndex:i] performClose:nil];
}
} }
- (void)dealloc - (void)dealloc
@ -1065,6 +1108,32 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
} }
} }
-(void) applyMenu:(NSMenu *)menu
{
if(menu == nullptr)
{
menu = [NSMenu new];
}
_menu = menu;
if ([self isKeyWindow])
{
auto appMenu = ::GetAppMenuItem();
if(appMenu != nullptr)
{
[[appMenu menu] removeItem:appMenu];
[_menu insertItem:appMenu atIndex:0];
_isAppMenuApplied = true;
}
[NSApp setMenu:menu];
}
}
-(void) setCanBecomeKeyAndMain -(void) setCanBecomeKeyAndMain
{ {
_canBecomeKeyAndMain = true; _canBecomeKeyAndMain = true;
@ -1157,6 +1226,24 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{ {
if([self activateAppropriateChild: true]) if([self activateAppropriateChild: true])
{ {
if(_menu == nullptr)
{
_menu = [NSMenu new];
}
auto appMenu = ::GetAppMenuItem();
if(appMenu != nullptr)
{
[[appMenu menu] removeItem:appMenu];
[_menu insertItem:appMenu atIndex:0];
_isAppMenuApplied = true;
}
[NSApp setMenu:_menu];
_parent->BaseEvents->Activated(); _parent->BaseEvents->Activated();
[super becomeKeyWindow]; [super becomeKeyWindow];
} }
@ -1201,6 +1288,28 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{ {
if(_parent) if(_parent)
_parent->BaseEvents->Deactivated(); _parent->BaseEvents->Deactivated();
auto appMenuItem = ::GetAppMenuItem();
if(appMenuItem != nullptr)
{
auto appMenu = ::GetAppMenu();
auto nativeAppMenu = dynamic_cast<AvnAppMenu*>(appMenu);
[[appMenuItem menu] removeItem:appMenuItem];
[nativeAppMenu->GetNative() addItem:appMenuItem];
[NSApp setMenu:nativeAppMenu->GetNative()];
}
else
{
[NSApp setMenu:nullptr];
}
// remove window menu items from appmenu?
[super resignKeyWindow]; [super resignKeyWindow];
} }

2
packages/Avalonia/AvaloniaBuildTasks.targets

@ -53,6 +53,7 @@
<PropertyGroup> <PropertyGroup>
<AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath> <AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath>
<AvaloniaXamlOriginalCopyFilePath Condition="'$(AvaloniaXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/original.dll</AvaloniaXamlOriginalCopyFilePath> <AvaloniaXamlOriginalCopyFilePath Condition="'$(AvaloniaXamlOriginalCopyFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/original.dll</AvaloniaXamlOriginalCopyFilePath>
<AvaloniaXamlIlVerifyIl Condition="'$(AvaloniaXamlIlVerifyIl)' == ''">false</AvaloniaXamlIlVerifyIl>
</PropertyGroup> </PropertyGroup>
<WriteLinesToFile <WriteLinesToFile
Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'" Condition="'$(_AvaloniaForceInternalMSBuild)' != 'true'"
@ -65,6 +66,7 @@
ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)" ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)"
OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)" OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)" ProjectDirectory="$(MSBuildProjectDirectory)"
VerifyIl="$(AvaloniaXamlIlVerifyIl)"
/> />
<Exec <Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'" Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"

8
readme.md

@ -8,9 +8,9 @@
## About ## About
**Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS. **Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), macOS and with experimental support for Android and iOS.
**Avalonia** is ready for **General-Purpose Desktop App Development**. However there may be some bugs and breaking changes as we continue along into this project's development. To see the status for some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239). **Avalonia** is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. To see the status of some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239).
| Control catalog | Desktop platforms | Mobile platforms | | Control catalog | Desktop platforms | Mobile platforms |
|---|---|---| |---|---|---|
@ -20,11 +20,11 @@
Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (<a href="http://avaloniaui.net/docs/quickstart/images/new-project-dialog.png">screenshot</a>). Now you can write code and markup that will work on multiple platforms! Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started. After installing it, open "New Project" dialog in Visual Studio, choose "Avalonia" in "Visual C#" section, select "Avalonia .NET Core Application" and press OK (<a href="http://avaloniaui.net/docs/quickstart/images/new-project-dialog.png">screenshot</a>). Now you can write code and markup that will work on multiple platforms!
For those without Visual Studio, starter guide for .NET Core CLI can be found [here](http://avaloniaui.net/docs/quickstart/create-new-project#net-core). For those without Visual Studio, a starter guide for .NET Core CLI can be found [here](http://avaloniaui.net/docs/quickstart/create-new-project#net-core).
Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed)) Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: ([stable(ish)](https://www.nuget.org/packages/Avalonia/), [nightly](https://github.com/AvaloniaUI/Avalonia/wiki/Using-nightly-build-feed))
Use these commands in Package Manager console to install Avalonia manually: Use these commands in the Package Manager console to install Avalonia manually:
``` ```
Install-Package Avalonia Install-Package Avalonia
Install-Package Avalonia.Desktop Install-Package Avalonia.Desktop

2
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -16,7 +16,7 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk> <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion> <TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

2
samples/ControlCatalog.Android/MainActivity.cs

@ -20,7 +20,7 @@ namespace ControlCatalog.Android
{ {
if (Avalonia.Application.Current == null) if (Avalonia.Application.Current == null)
{ {
AppBuilder.Configure(new App()) AppBuilder.Configure<App>()
.UseAndroid() .UseAndroid()
.SetupWithoutStarting(); .SetupWithoutStarting();
Content = new MainView(); Content = new MainView();

6
samples/ControlCatalog.NetCore/Program.cs

@ -55,7 +55,11 @@ namespace ControlCatalog.NetCore
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>() => AppBuilder.Configure<App>()
.UsePlatformDetect() .UsePlatformDetect()
.With(new X11PlatformOptions { EnableMultiTouch = true }) .With(new X11PlatformOptions
{
EnableMultiTouch = true,
UseDBusMenu = true
})
.With(new Win32PlatformOptions .With(new Win32PlatformOptions
{ {
EnableMultitouch = true, EnableMultitouch = true,

18
samples/ControlCatalog/App.xaml

@ -2,9 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App"> x:Class="ControlCatalog.App">
<Application.Styles> <Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/> <StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/> <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<Style Selector="TextBlock.h1"> <Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/> <Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/> <Setter Property="FontWeight" Value="Medium"/>
@ -17,4 +17,16 @@
</Style> </Style>
<StyleInclude Source="/SideBar.xaml"/> <StyleInclude Source="/SideBar.xaml"/>
</Application.Styles> </Application.Styles>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="Open" Clicked="OnOpenClicked"/>
<NativeMenuItem Header="Recent">
<NativeMenuItem.Menu>
<NativeMenu/>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/>
</NativeMenu>
</NativeMenu.Menu>
</Application> </Application>

13
samples/ControlCatalog/App.xaml.cs

@ -1,4 +1,6 @@
using System;
using Avalonia; using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
@ -6,9 +8,20 @@ namespace ControlCatalog
{ {
public class App : Application public class App : Application
{ {
private NativeMenu _recentMenu;
public override void Initialize() public override void Initialize()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
Name = "Avalonia";
_recentMenu = (NativeMenu.GetMenu(this).Items[1] as NativeMenuItem).Menu;
}
public void OnOpenClicked(object sender, EventArgs args)
{
_recentMenu.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()

25
samples/ControlCatalog/DecoratedWindow.xaml

@ -3,6 +3,31 @@
x:Class="ControlCatalog.DecoratedWindow" x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery" Title="Avalonia Control Gallery"
xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window"> xmlns:local="clr-namespace:ControlCatalog" HasSystemDecorations="False" Name="Window">
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="Decorated">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Open"/>
<NativeMenuItem Header="Recent">
<NativeMenuItem.Menu>
<NativeMenu/>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItem Header="Quit Avalonia" Gesture="CMD+Q"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItem Header="Edit">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Copy"/>
<NativeMenuItem Header="Paste"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
<Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5"> <Grid RowDefinitions="5,*,5" ColumnDefinitions="5,*,5">
<DockPanel Grid.Column="1" Grid.Row="1" > <DockPanel Grid.Column="1" Grid.Row="1" >
<Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto"> <Grid Name="TitleBar" Background="LightBlue" DockPanel.Dock="Top" ColumnDefinitions="Auto,*,Auto">

8
samples/ControlCatalog/DecoratedWindow.xaml.cs

@ -18,18 +18,18 @@ namespace ControlCatalog
{ {
var ctl = this.FindControl<Control>(name); var ctl = this.FindControl<Control>(name);
ctl.Cursor = new Cursor(cursor); ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += delegate ctl.PointerPressed += (i, e) =>
{ {
PlatformImpl?.BeginResizeDrag(edge); PlatformImpl?.BeginResizeDrag(edge, e);
}; };
} }
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
this.FindControl<Control>("TitleBar").PointerPressed += delegate this.FindControl<Control>("TitleBar").PointerPressed += (i, e) =>
{ {
PlatformImpl?.BeginMoveDrag(); PlatformImpl?.BeginMoveDrag(e);
}; };
SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West); SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West);
SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East); SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East);

31
samples/ControlCatalog/MainWindow.xaml

@ -8,7 +8,36 @@
xmlns:vm="clr-namespace:ControlCatalog.ViewModels" xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views" xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow"> x:Class="ControlCatalog.MainWindow">
<Window.DataTemplates>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="File">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Open" Clicked="OnOpenClicked"/>
<NativeMenuItemSeperator/>
<NativeMenuItem Header="Recent">
<NativeMenuItem.Menu>
<NativeMenu/>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItemSeperator/>
<NativeMenuItem Header="Quit Avalonia" Clicked="OnCloseClicked" Gesture="CMD+Q"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItem Header="Edit">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Copy"/>
<NativeMenuItem Header="Paste"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
<Window.DataTemplates>
<DataTemplate DataType="vm:NotificationViewModel"> <DataTemplate DataType="vm:NotificationViewModel">
<v:CustomNotificationView /> <v:CustomNotificationView />
</DataTemplate> </DataTemplate>

14
samples/ControlCatalog/MainWindow.xaml.cs

@ -14,6 +14,7 @@ namespace ControlCatalog
public class MainWindow : Window public class MainWindow : Window
{ {
private WindowNotificationManager _notificationArea; private WindowNotificationManager _notificationArea;
private NativeMenu _recentMenu;
public MainWindow() public MainWindow()
{ {
@ -29,8 +30,21 @@ namespace ControlCatalog
}; };
DataContext = new MainWindowViewModel(_notificationArea); DataContext = new MainWindowViewModel(_notificationArea);
_recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu;
} }
public void OnOpenClicked(object sender, EventArgs args)
{
_recentMenu.Items.Insert(0, new NativeMenuItem("Item " + (_recentMenu.Items.Count + 1)));
}
public void OnCloseClicked(object sender, EventArgs args)
{
Close();
}
private void InitializeComponent() private void InitializeComponent()
{ {
// TODO: iOS does not support dynamically loading assemblies // TODO: iOS does not support dynamically loading assemblies

5
samples/ControlCatalog/Pages/MenuPage.xaml

@ -3,8 +3,11 @@
x:Class="ControlCatalog.Pages.MenuPage"> x:Class="ControlCatalog.Pages.MenuPage">
<StackPanel Orientation="Vertical" Spacing="4"> <StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">Menu</TextBlock> <TextBlock Classes="h1">Menu</TextBlock>
<TextBlock Classes="h2">Exported menu fallback</TextBlock>
<TextBlock>(Should be only visible on platforms without desktop-global menu bar)</TextBlock>
<NativeMenuBar/>
<TextBlock Classes="h2">A window menu</TextBlock> <TextBlock Classes="h2">A window menu</TextBlock>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"

6
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -17,7 +17,7 @@ namespace Avalonia
{ {
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new() public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{ {
builder.UseWindowingSubsystem(() => Android.AndroidPlatform.Initialize(builder.Instance), "Android"); builder.UseWindowingSubsystem(() => Android.AndroidPlatform.Initialize(builder.ApplicationType), "Android");
builder.UseSkia(); builder.UseSkia();
return builder; return builder;
} }
@ -41,7 +41,7 @@ namespace Avalonia.Android
_scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity; _scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
} }
public static void Initialize(Avalonia.Application app) public static void Initialize(Type appType)
{ {
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToTransient<ClipboardImpl>() .Bind<IClipboard>().ToTransient<ClipboardImpl>()
@ -55,7 +55,7 @@ namespace Avalonia.Android
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop()) .Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>() .Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly)); .Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly));
SkiaPlatform.Initialize(); SkiaPlatform.Initialize();
((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext) ((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext)

2
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,6 +1,6 @@
<Project Sdk="MSBuild.Sdk.Extras"> <Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup> <PropertyGroup>
<TargetFramework>monoandroid80</TargetFramework> <TargetFramework>monoandroid90</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

2
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -16,7 +16,7 @@
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> <AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk> <AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.0</TargetFrameworkVersion> <TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

23
src/Avalonia.Base/AvaloniaObject.cs

@ -326,8 +326,6 @@ namespace Avalonia
VerifyAccess(); VerifyAccess();
var description = GetDescription(source);
if (property.IsDirect) if (property.IsDirect)
{ {
if (property.IsReadOnly) if (property.IsReadOnly)
@ -335,12 +333,12 @@ namespace Avalonia
throw new ArgumentException($"The property {property.Name} is readonly."); throw new ArgumentException($"The property {property.Name} is readonly.");
} }
Logger.Verbose( Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property, LogArea.Property,
this, this,
"Bound {Property} to {Binding} with priority LocalValue", "Bound {Property} to {Binding} with priority LocalValue",
property, property,
description); GetDescription(source));
if (_directBindings == null) if (_directBindings == null)
{ {
@ -351,12 +349,12 @@ namespace Avalonia
} }
else else
{ {
Logger.Verbose( Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property, LogArea.Property,
this, this,
"Bound {Property} to {Binding} with priority {Priority}", "Bound {Property} to {Binding} with priority {Priority}",
property, property,
description, GetDescription(source),
priority); priority);
return Values.AddBinding(property, source, priority); return Values.AddBinding(property, source, priority);
@ -406,7 +404,7 @@ namespace Avalonia
{ {
RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority); RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority);
Logger.Verbose( Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property, LogArea.Property,
this, this,
"{Property} changed from {$Old} to {$Value} with priority {Priority}", "{Property} changed from {$Old} to {$Value} with priority {Priority}",
@ -458,8 +456,7 @@ namespace Avalonia
/// <param name="e">The binding error.</param> /// <param name="e">The binding error.</param>
protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e) protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e)
{ {
Logger.Log( Logger.TryGet(LogEventLevel.Warning)?.Log(
LogEventLevel.Warning,
LogArea.Binding, LogArea.Binding,
this, this,
"Error in binding to {Target}.{Property}: {Message}", "Error in binding to {Target}.{Property}: {Message}",
@ -812,7 +809,7 @@ namespace Avalonia
/// <param name="priority">The priority.</param> /// <param name="priority">The priority.</param>
private void LogPropertySet(AvaloniaProperty property, object value, BindingPriority priority) private void LogPropertySet(AvaloniaProperty property, object value, BindingPriority priority)
{ {
Logger.Verbose( Logger.TryGet(LogEventLevel.Verbose)?.Log(
LogArea.Property, LogArea.Property,
this, this,
"Set {Property} to {$Value} with priority {Priority}", "Set {Property} to {$Value} with priority {Priority}",

9
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -192,9 +192,9 @@ namespace Avalonia
{ {
return observable.Subscribe(e => return observable.Subscribe(e =>
{ {
if (e.Sender is TTarget) if (e.Sender is TTarget target)
{ {
action((TTarget)e.Sender, e); action(target, e);
} }
}); });
} }
@ -207,6 +207,7 @@ namespace Avalonia
/// <param name="observable">The property changed observable.</param> /// <param name="observable">The property changed observable.</param>
/// <param name="handler">Given a TTarget, returns the handler.</param> /// <param name="handler">Given a TTarget, returns the handler.</param>
/// <returns>A disposable that can be used to terminate the subscription.</returns> /// <returns>A disposable that can be used to terminate the subscription.</returns>
[Obsolete("Use overload taking Action<TTarget, AvaloniaPropertyChangedEventArgs>.")]
public static IDisposable AddClassHandler<TTarget>( public static IDisposable AddClassHandler<TTarget>(
this IObservable<AvaloniaPropertyChangedEventArgs> observable, this IObservable<AvaloniaPropertyChangedEventArgs> observable,
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler) Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
@ -238,9 +239,7 @@ namespace Avalonia
Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler) Func<TTarget, Action<AvaloniaPropertyChangedEventArgs>> handler)
where TTarget : class where TTarget : class
{ {
var target = e.Sender as TTarget; if (e.Sender is TTarget target)
if (target != null)
{ {
handler(target)(e); handler(target)(e);
} }

2
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -165,7 +165,7 @@ namespace Avalonia.Data.Core
} }
else else
{ {
Logger.Error( Logger.TryGet(LogEventLevel.Error)?.Log(
LogArea.Binding, LogArea.Binding,
this, this,
"Could not convert FallbackValue {FallbackValue} to {Type}", "Could not convert FallbackValue {FallbackValue} to {Type}",

71
src/Avalonia.Base/Logging/ILogSink.cs

@ -8,6 +8,77 @@ namespace Avalonia.Logging
/// </summary> /// </summary>
public interface ILogSink public interface ILogSink
{ {
/// <summary>
/// Checks if given log level is enabled.
/// </summary>
/// <param name="level">The log event level.</param>
/// <returns><see langword="true"/> if given log level is enabled.</returns>
bool IsEnabled(LogEventLevel level);
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
void Log(
LogEventLevel level,
string area,
object source,
string messageTemplate);
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
void Log<T0>(
LogEventLevel level,
string area,
object source,
string messageTemplate,
T0 propertyValue0);
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
void Log<T0, T1>(
LogEventLevel level,
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1);
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
/// <param name="propertyValue2">Message property value.</param>
void Log<T0, T1, T2>(
LogEventLevel level,
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2);
/// <summary> /// <summary>
/// Logs a new event. /// Logs a new event.
/// </summary> /// </summary>

125
src/Avalonia.Base/Logging/Logger.cs

@ -1,8 +1,6 @@
// Copyright (c) The Avalonia Project. All rights reserved. // Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Runtime.CompilerServices;
namespace Avalonia.Logging namespace Avalonia.Logging
{ {
/// <summary> /// <summary>
@ -16,124 +14,43 @@ namespace Avalonia.Logging
public static ILogSink Sink { get; set; } public static ILogSink Sink { get; set; }
/// <summary> /// <summary>
/// Logs an event. /// Checks if given log level is enabled.
/// </summary> /// </summary>
/// <param name="level">The log event level.</param> /// <param name="level">The log event level.</param>
/// <param name="area">The area that the event originates.</param> /// <returns><see langword="true"/> if given log level is enabled.</returns>
/// <param name="source">The object from which the event originates.</param> public static bool IsEnabled(LogEventLevel level)
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Log(
LogEventLevel level,
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{
Sink?.Log(level, area, source, messageTemplate, propertyValues);
}
/// <summary>
/// Logs an event with the <see cref="LogEventLevel.Verbose"/> level.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Verbose(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{ {
Log(LogEventLevel.Verbose, area, source, messageTemplate, propertyValues); return Sink?.IsEnabled(level) == true;
} }
/// <summary> /// <summary>
/// Logs an event with the <see cref="LogEventLevel.Debug"/> level. /// Returns parametrized logging sink if given log level is enabled.
/// </summary> /// </summary>
/// <param name="area">The area that the event originates.</param> /// <param name="level">The log event level.</param>
/// <param name="source">The object from which the event originates.</param> /// <returns>Log sink or <see langword="null"/> if log level is not enabled.</returns>
/// <param name="messageTemplate">The message template.</param> public static ParametrizedLogger? TryGet(LogEventLevel level)
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Debug(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{ {
Log(LogEventLevel.Debug, area, source, messageTemplate, propertyValues); if (!IsEnabled(level))
} {
return null;
}
/// <summary> return new ParametrizedLogger(Sink, level);
/// Logs an event with the <see cref="LogEventLevel.Information"/> level.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Information(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{
Log(LogEventLevel.Information, area, source, messageTemplate, propertyValues);
} }
/// <summary> /// <summary>
/// Logs an event with the <see cref="LogEventLevel.Warning"/> level. /// Returns parametrized logging sink if given log level is enabled.
/// </summary> /// </summary>
/// <param name="area">The area that the event originates.</param> /// <param name="level">The log event level.</param>
/// <param name="source">The object from which the event originates.</param> /// <param name="outLogger">Log sink that is valid only if method returns <see langword="true"/>.</param>
/// <param name="messageTemplate">The message template.</param> /// <returns><see langword="true"/> if logger was obtained successfully.</returns>
/// <param name="propertyValues">The message property values.</param> public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Warning(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{ {
Log(LogEventLevel.Warning, area, source, messageTemplate, propertyValues); ParametrizedLogger? logger = TryGet(level);
}
/// <summary> outLogger = logger.GetValueOrDefault();
/// Logs an event with the <see cref="LogEventLevel.Error"/> level.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Error(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{
Log(LogEventLevel.Error, area, source, messageTemplate, propertyValues);
}
/// <summary> return logger.HasValue;
/// Logs an event with the <see cref="LogEventLevel.Fatal"/> level.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValues">The message property values.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Fatal(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
{
Log(LogEventLevel.Fatal, area, source, messageTemplate, propertyValues);
} }
} }
} }

174
src/Avalonia.Base/Logging/ParametrizedLogger.cs

@ -0,0 +1,174 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Runtime.CompilerServices;
namespace Avalonia.Logging
{
/// <summary>
/// Logger sink parametrized for given logging level.
/// </summary>
public readonly struct ParametrizedLogger
{
private readonly ILogSink _sink;
private readonly LogEventLevel _level;
public ParametrizedLogger(ILogSink sink, LogEventLevel level)
{
_sink = sink;
_level = level;
}
/// <summary>
/// Checks if this logger can be used.
/// </summary>
public bool IsValid => _sink != null;
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log(
string area,
object source,
string messageTemplate)
{
_sink.Log(_level, area, source, messageTemplate);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0>(
string area,
object source,
string messageTemplate,
T0 propertyValue0)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
/// <param name="propertyValue2">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
/// <param name="propertyValue2">Message property value.</param>
/// <param name="propertyValue3">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2,
T3 propertyValue3)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
/// <param name="propertyValue2">Message property value.</param>
/// <param name="propertyValue3">Message property value.</param>
/// <param name="propertyValue4">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3, T4>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2,
T3 propertyValue3,
T4 propertyValue4)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4);
}
/// <summary>
/// Logs an event.
/// </summary>
/// <param name="area">The area that the event originates.</param>
/// <param name="source">The object from which the event originates.</param>
/// <param name="messageTemplate">The message template.</param>
/// <param name="propertyValue0">Message property value.</param>
/// <param name="propertyValue1">Message property value.</param>
/// <param name="propertyValue2">Message property value.</param>
/// <param name="propertyValue3">Message property value.</param>
/// <param name="propertyValue4">Message property value.</param>
/// <param name="propertyValue5">Message property value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Log<T0, T1, T2, T3, T4, T5>(
string area,
object source,
string messageTemplate,
T0 propertyValue0,
T1 propertyValue1,
T2 propertyValue2,
T3 propertyValue3,
T4 propertyValue4,
T5 propertyValue5)
{
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4, propertyValue5);
}
}
}

2
src/Avalonia.Base/PriorityValue.cs

@ -301,7 +301,7 @@ namespace Avalonia
} }
else else
{ {
Logger.Error( Logger.TryGet(LogEventLevel.Error)?.Log(
LogArea.Binding, LogArea.Binding,
Owner, Owner,
"Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})",

4
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@ -34,7 +34,7 @@ namespace Avalonia.Build.Tasks
var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input, var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(), File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath); ProjectDirectory, OutputPath, VerifyIl);
if (!res.Success) if (!res.Success)
return false; return false;
if (!res.WrittenFile) if (!res.WrittenFile)
@ -66,6 +66,8 @@ namespace Avalonia.Build.Tasks
public string ProjectDirectory { get; set; } public string ProjectDirectory { get; set; }
public string OutputPath { get; set; } public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
public IBuildEngine BuildEngine { get; set; } public IBuildEngine BuildEngine { get; set; }
public ITaskHost HostObject { get; set; } public ITaskHost HostObject { get; set; }

3
src/Avalonia.Build.Tasks/Program.cs

@ -28,7 +28,8 @@ namespace Avalonia.Build.Tasks
ReferencesFilePath = args[1], ReferencesFilePath = args[1],
OutputPath = args[2], OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(), BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory() ProjectDirectory = Directory.GetCurrentDirectory(),
VerifyIl = true
}.Execute() ? }.Execute() ?
0 : 0 :
2; 2;

4
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -40,7 +40,7 @@ namespace Avalonia.Build.Tasks
} }
public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory,
string output) string output, bool verifyIl)
{ {
var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input); var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input);
var asm = typeSystem.TargetAssemblyDefinition; var asm = typeSystem.TargetAssemblyDefinition;
@ -65,7 +65,7 @@ namespace Avalonia.Build.Tasks
var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem, var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage); xamlLanguage);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass); var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass) { EnableIlVerification = verifyIl };
var editorBrowsableAttribute = typeSystem var editorBrowsableAttribute = typeSystem
.GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute")) .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute"))

50
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -723,29 +723,29 @@ namespace Avalonia.Controls
PseudoClass<DataGrid, bool>(IsValidProperty, x => !x, ":invalid"); PseudoClass<DataGrid, bool>(IsValidProperty, x => !x, ":invalid");
ItemsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnItemsPropertyChanged); ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnCanUserResizeColumnsChanged); CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnColumnWidthChanged); ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));
RowBackgroundProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowBackgroundChanged); RowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
AlternatingRowBackgroundProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowBackgroundChanged); AlternatingRowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>(x => x.OnFrozenColumnCountChanged); FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e));
GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>(x => x.OnGridLinesVisibilityChanged); GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnGridLinesVisibilityChanged(e));
HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>(x => x.OnHeadersVisibilityChanged); HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHeadersVisibilityChanged(e));
HorizontalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>(x => x.OnHorizontalGridLinesBrushChanged); HorizontalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHorizontalGridLinesBrushChanged(e));
IsReadOnlyProperty.Changed.AddClassHandler<DataGrid>(x => x.OnIsReadOnlyChanged); IsReadOnlyProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnIsReadOnlyChanged(e));
MaxColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnMaxColumnWidthChanged); MaxColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMaxColumnWidthChanged(e));
MinColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnMinColumnWidthChanged); MinColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMinColumnWidthChanged(e));
RowHeightProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowHeightChanged); RowHeightProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeightChanged(e));
RowHeaderWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowHeaderWidthChanged); RowHeaderWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeaderWidthChanged(e));
SelectionModeProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectionModeChanged); SelectionModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectionModeChanged(e));
VerticalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>(x => x.OnVerticalGridLinesBrushChanged); VerticalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnVerticalGridLinesBrushChanged(e));
SelectedIndexProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectedIndexChanged); SelectedIndexProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedIndexChanged(e));
SelectedItemProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectedItemChanged); SelectedItemProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedItemChanged(e));
IsEnabledProperty.Changed.AddClassHandler<DataGrid>(x => x.DataGrid_IsEnabledChanged); IsEnabledProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.DataGrid_IsEnabledChanged(e));
AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler<DataGrid>(x => x.OnAreRowGroupHeadersFrozenChanged); AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAreRowGroupHeadersFrozenChanged(e));
RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowDetailsTemplateChanged); RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsTemplateChanged(e));
RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowDetailsVisibilityModeChanged); RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnAutoGenerateColumnsChanged); AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAutoGenerateColumnsChanged(e));
} }
/// <summary> /// <summary>
@ -3533,7 +3533,7 @@ namespace Avalonia.Controls
if (AreColumnHeadersVisible && if (AreColumnHeadersVisible &&
_vScrollBar != null && _vScrollBar.IsVisible) _vScrollBar != null && _vScrollBar.IsVisible)
{ {
_topRightCornerHeader.IsVisible = true; ; _topRightCornerHeader.IsVisible = true;
} }
else else
{ {
@ -5594,7 +5594,7 @@ namespace Avalonia.Controls
{ {
// This will trigger a call to this method via Cells_SizeChanged for // This will trigger a call to this method via Cells_SizeChanged for
// which no processing is needed. // which no processing is needed.
_vScrollBar.IsVisible = true; ; _vScrollBar.IsVisible = true;
if (_vScrollBar.DesiredSize.Width == 0) if (_vScrollBar.DesiredSize.Width == 0)
{ {
// We need to know the width for the rest of layout to work correctly so measure it now // We need to know the width for the rest of layout to work correctly so measure it now

4
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@ -29,7 +29,7 @@ namespace Avalonia.Controls
static DataGridCell() static DataGridCell()
{ {
PointerPressedEvent.AddClassHandler<DataGridCell>( PointerPressedEvent.AddClassHandler<DataGridCell>(
x => x.DataGridCell_PointerPressed, handledEventsToo: true); (x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
} }
public DataGridCell() public DataGridCell()
{ } { }
@ -219,4 +219,4 @@ namespace Avalonia.Controls
} }
} }
} }
} }

4
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@ -67,7 +67,7 @@ namespace Avalonia.Controls
static DataGridColumnHeader() static DataGridColumnHeader()
{ {
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>(x => x.OnAreSeparatorsVisibleChanged); AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x,e) => x.OnAreSeparatorsVisibleChanged(e));
} }
/// <summary> /// <summary>
@ -468,7 +468,7 @@ namespace Avalonia.Controls
private void DataGridColumnHeader_PointerReleased(object sender, PointerReleasedEventArgs e) private void DataGridColumnHeader_PointerReleased(object sender, PointerReleasedEventArgs e)
{ {
if (OwningColumn == null || e.Handled || !IsEnabled || e.MouseButton != MouseButton.Left) if (OwningColumn == null || e.Handled || !IsEnabled || e.InitialPressMouseButton != MouseButton.Left)
{ {
return; return;
} }

8
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -116,10 +116,10 @@ namespace Avalonia.Controls
static DataGridRow() static DataGridRow()
{ {
HeaderProperty.Changed.AddClassHandler<DataGridRow>(x => x.OnHeaderChanged); HeaderProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnHeaderChanged(e));
DetailsTemplateProperty.Changed.AddClassHandler<DataGridRow>(x => x.OnDetailsTemplateChanged); DetailsTemplateProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnDetailsTemplateChanged(e));
AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>(x => x.OnAreDetailsVisibleChanged); AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e));
PointerPressedEvent.AddClassHandler<DataGridRow>(x => x.DataGridRow_PointerPressed, handledEventsToo: true); PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@ -109,7 +109,7 @@ namespace Avalonia.Controls
static DataGridRowGroupHeader() static DataGridRowGroupHeader()
{ {
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>(x => x.OnSublevelIndentChanged); SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>((x,e) => x.OnSublevelIndentChanged(e));
} }
/// <summary> /// <summary>
@ -446,4 +446,4 @@ namespace Avalonia.Controls
} }
} }
} }

50
src/Avalonia.Controls/AppBuilderBase.cs

@ -18,7 +18,9 @@ namespace Avalonia.Controls
{ {
private static bool s_setupWasAlreadyCalled; private static bool s_setupWasAlreadyCalled;
private Action _optionsInitializers; private Action _optionsInitializers;
private Func<Application> _appFactory;
private IApplicationLifetime _lifetime;
/// <summary> /// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance. /// Gets or sets the <see cref="IRuntimePlatform"/> instance.
/// </summary> /// </summary>
@ -30,10 +32,15 @@ namespace Avalonia.Controls
public Action RuntimePlatformServicesInitializer { get; private set; } public Action RuntimePlatformServicesInitializer { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the <see cref="Application"/> instance being initialized. /// Gets the <see cref="Application"/> instance being initialized.
/// </summary> /// </summary>
public Application Instance { get; protected set; } public Application Instance { get; private set; }
/// <summary>
/// Gets the type of the Instance (even if it's not created yet)
/// </summary>
public Type ApplicationType { get; private set; }
/// <summary> /// <summary>
/// Gets or sets a method to call the initialize the windowing subsystem. /// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary> /// </summary>
@ -76,20 +83,11 @@ namespace Avalonia.Controls
public static TAppBuilder Configure<TApp>() public static TAppBuilder Configure<TApp>()
where TApp : Application, new() where TApp : Application, new()
{ {
return Configure(new TApp());
}
/// <summary>
/// Begin configuring an <see cref="Application"/>.
/// </summary>
/// <returns>An <typeparamref name="TAppBuilder"/> instance.</returns>
public static TAppBuilder Configure(Application app)
{
AvaloniaLocator.CurrentMutable.BindToSelf(app);
return new TAppBuilder() return new TAppBuilder()
{ {
Instance = app, ApplicationType = typeof(TApp),
// Needed for CoreRT compatibility
_appFactory = () => new TApp()
}; };
} }
@ -157,6 +155,18 @@ namespace Avalonia.Controls
return Self; return Self;
} }
/// <summary>
/// Sets up the platform-specific services for the application and initialized it with a particular lifetime, but does not run it.
/// </summary>
/// <param name="lifetime"></param>
/// <returns></returns>
public TAppBuilder SetupWithLifetime(IApplicationLifetime lifetime)
{
_lifetime = lifetime;
Setup();
return Self;
}
/// <summary> /// <summary>
/// Specifies a windowing subsystem to use. /// Specifies a windowing subsystem to use.
/// </summary> /// </summary>
@ -254,11 +264,6 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
private void Setup() private void Setup()
{ {
if (Instance == null)
{
throw new InvalidOperationException("No App instance configured.");
}
if (RuntimePlatformServicesInitializer == null) if (RuntimePlatformServicesInitializer == null)
{ {
throw new InvalidOperationException("No runtime platform services configured."); throw new InvalidOperationException("No runtime platform services configured.");
@ -285,6 +290,9 @@ namespace Avalonia.Controls
WindowingSubsystemInitializer(); WindowingSubsystemInitializer();
RenderingSubsystemInitializer(); RenderingSubsystemInitializer();
AfterPlatformServicesSetupCallback(Self); AfterPlatformServicesSetupCallback(Self);
Instance = _appFactory();
Instance.ApplicationLifetime = _lifetime;
AvaloniaLocator.CurrentMutable.BindToSelf(Instance);
Instance.RegisterServices(); Instance.RegisterServices();
Instance.Initialize(); Instance.Initialize();
AfterSetupCallback(Self); AfterSetupCallback(Self);

19
src/Avalonia.Controls/Application.cs

@ -32,7 +32,7 @@ namespace Avalonia
/// method. /// method.
/// - Tracks the lifetime of the application. /// - Tracks the lifetime of the application.
/// </remarks> /// </remarks>
public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode public class Application : AvaloniaObject, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
{ {
/// <summary> /// <summary>
/// The application-global data templates. /// The application-global data templates.
@ -210,5 +210,22 @@ namespace Avalonia
{ {
ResourcesChanged?.Invoke(this, e); ResourcesChanged?.Invoke(this, e);
} }
private string _name;
/// <summary>
/// Defines Name property
/// </summary>
public static readonly DirectProperty<Application, string> NameProperty =
AvaloniaProperty.RegisterDirect<Application, string>("Name", o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Application name to be used for various platform-specific purposes
/// </summary>
public string Name
{
get => _name;
set => SetAndRaise(NameProperty, ref _name, value);
}
} }
} }

3
src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs

@ -125,8 +125,7 @@ namespace Avalonia
where T : AppBuilderBase<T>, new() where T : AppBuilderBase<T>, new()
{ {
var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode}; var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode};
builder.Instance.ApplicationLifetime = lifetime; builder.SetupWithLifetime(lifetime);
builder.SetupWithoutStarting();
return lifetime.Start(args); return lifetime.Start(args);
} }
} }

18
src/Avalonia.Controls/AutoCompleteBox.cs

@ -805,15 +805,15 @@ namespace Avalonia.Controls
{ {
FocusableProperty.OverrideDefaultValue<AutoCompleteBox>(true); FocusableProperty.OverrideDefaultValue<AutoCompleteBox>(true);
MinimumPopulateDelayProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnMinimumPopulateDelayChanged); MinimumPopulateDelayProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnMinimumPopulateDelayChanged(e));
IsDropDownOpenProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnIsDropDownOpenChanged); IsDropDownOpenProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnIsDropDownOpenChanged(e));
SelectedItemProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnSelectedItemPropertyChanged); SelectedItemProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnSelectedItemPropertyChanged(e));
TextProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnTextPropertyChanged); TextProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnTextPropertyChanged(e));
SearchTextProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnSearchTextPropertyChanged); SearchTextProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnSearchTextPropertyChanged(e));
FilterModeProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnFilterModePropertyChanged); FilterModeProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnFilterModePropertyChanged(e));
ItemFilterProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnItemFilterPropertyChanged); ItemFilterProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemFilterPropertyChanged(e));
ItemsProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnItemsPropertyChanged); ItemsProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnItemsPropertyChanged(e));
IsEnabledProperty.Changed.AddClassHandler<AutoCompleteBox>(x => x.OnControlIsEnabledChanged); IsEnabledProperty.Changed.AddClassHandler<AutoCompleteBox>((x,e) => x.OnControlIsEnabledChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Button.cs

@ -294,7 +294,7 @@ namespace Avalonia.Controls
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (IsPressed && e.MouseButton == MouseButton.Left) if (IsPressed && e.InitialPressMouseButton == MouseButton.Left)
{ {
IsPressed = false; IsPressed = false;
e.Handled = true; e.Handled = true;

25
src/Avalonia.Controls/Calendar/Calendar.cs

@ -1565,7 +1565,7 @@ namespace Avalonia.Controls
protected override void OnPointerReleased(PointerReleasedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (!HasFocusInternal && e.MouseButton == MouseButton.Left) if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left)
{ {
FocusManager.Instance.Focus(this); FocusManager.Instance.Focus(this);
} }
@ -2057,18 +2057,17 @@ namespace Avalonia.Controls
static Calendar() static Calendar()
{ {
IsEnabledProperty.Changed.AddClassHandler<Calendar>(x => x.OnIsEnabledChanged); IsEnabledProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnIsEnabledChanged(e));
FirstDayOfWeekProperty.Changed.AddClassHandler<Calendar>(x => x.OnFirstDayOfWeekChanged); FirstDayOfWeekProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnFirstDayOfWeekChanged(e));
IsTodayHighlightedProperty.Changed.AddClassHandler<Calendar>(x => x.OnIsTodayHighlightedChanged); IsTodayHighlightedProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnIsTodayHighlightedChanged(e));
DisplayModeProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayModePropertyChanged); DisplayModeProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayModePropertyChanged(e));
SelectionModeProperty.Changed.AddClassHandler<Calendar>(x => x.OnSelectionModeChanged); SelectionModeProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnSelectionModeChanged(e));
SelectedDateProperty.Changed.AddClassHandler<Calendar>(x => x.OnSelectedDateChanged); SelectedDateProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnSelectedDateChanged(e));
DisplayDateProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateChanged); DisplayDateProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateStartChanged); DisplayDateStartProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<Calendar>(x => x.OnDisplayDateEndChanged); DisplayDateEndProperty.Changed.AddClassHandler<Calendar>((x,e) => x.OnDisplayDateEndChanged(e));
KeyDownEvent.AddClassHandler<Calendar>(x => x.Calendar_KeyDown); KeyDownEvent.AddClassHandler<Calendar>((x,e) => x.Calendar_KeyDown(e));
KeyUpEvent.AddClassHandler<Calendar>(x => x.Calendar_KeyUp); KeyUpEvent.AddClassHandler<Calendar>((x,e) => x.Calendar_KeyUp(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Calendar/CalendarButton.cs

@ -173,7 +173,7 @@ namespace Avalonia.Controls.Primitives
protected override void OnPointerReleased(PointerReleasedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left) if (e.InitialPressMouseButton == MouseButton.Left)
CalendarLeftMouseButtonUp?.Invoke(this, e); CalendarLeftMouseButtonUp?.Invoke(this, e);
} }
} }

2
src/Avalonia.Controls/Calendar/CalendarDayButton.cs

@ -231,7 +231,7 @@ namespace Avalonia.Controls.Primitives
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left) if (e.InitialPressMouseButton == MouseButton.Left)
CalendarDayButtonMouseUp?.Invoke(this, e); CalendarDayButtonMouseUp?.Invoke(this, e);
} }
} }

16
src/Avalonia.Controls/Calendar/DatePicker.cs

@ -393,14 +393,14 @@ namespace Avalonia.Controls
{ {
FocusableProperty.OverrideDefaultValue<DatePicker>(true); FocusableProperty.OverrideDefaultValue<DatePicker>(true);
DisplayDateProperty.Changed.AddClassHandler<DatePicker>(x => x.OnDisplayDateChanged); DisplayDateProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<DatePicker>(x => x.OnDisplayDateStartChanged); DisplayDateStartProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<DatePicker>(x => x.OnDisplayDateEndChanged); DisplayDateEndProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateEndChanged(e));
IsDropDownOpenProperty.Changed.AddClassHandler<DatePicker>(x => x.OnIsDropDownOpenChanged); IsDropDownOpenProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnIsDropDownOpenChanged(e));
SelectedDateProperty.Changed.AddClassHandler<DatePicker>(x => x.OnSelectedDateChanged); SelectedDateProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnSelectedDateChanged(e));
SelectedDateFormatProperty.Changed.AddClassHandler<DatePicker>(x => x.OnSelectedDateFormatChanged); SelectedDateFormatProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnSelectedDateFormatChanged(e));
CustomDateFormatStringProperty.Changed.AddClassHandler<DatePicker>(x => x.OnCustomDateFormatStringChanged); CustomDateFormatStringProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnCustomDateFormatStringChanged(e));
TextProperty.Changed.AddClassHandler<DatePicker>(x => x.OnTextChanged); TextProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnTextChanged(e));
} }
/// <summary> /// <summary>
/// Initializes a new instance of the /// Initializes a new instance of the

12
src/Avalonia.Controls/Carousel.cs

@ -84,17 +84,5 @@ namespace Avalonia.Controls
--SelectedIndex; --SelectedIndex;
} }
} }
/// <inheritdoc/>
protected override void OnKeyDown(KeyEventArgs e)
{
// Ignore key presses.
}
/// <inheritdoc/>
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
// Ignore pointer presses.
}
} }
} }

4
src/Avalonia.Controls/ComboBox.cs

@ -65,8 +65,8 @@ namespace Avalonia.Controls
{ {
ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<ComboBox>(DefaultPanel);
FocusableProperty.OverrideDefaultValue<ComboBox>(true); FocusableProperty.OverrideDefaultValue<ComboBox>(true);
SelectedItemProperty.Changed.AddClassHandler<ComboBox>(x => x.SelectedItemChanged); SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x,e) => x.SelectedItemChanged(e));
KeyDownEvent.AddClassHandler<ComboBox>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel); KeyDownEvent.AddClassHandler<ComboBox>((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/ContentControl.cs

@ -43,7 +43,7 @@ namespace Avalonia.Controls
static ContentControl() static ContentControl()
{ {
ContentProperty.Changed.AddClassHandler<ContentControl>(x => x.ContentChanged); ContentProperty.Changed.AddClassHandler<ContentControl>((x, e) => x.ContentChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/ContextMenu.cs

@ -192,7 +192,7 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
} }
if (e.MouseButton == MouseButton.Right) if (e.InitialPressMouseButton == MouseButton.Right)
{ {
if (contextMenu.CancelOpening()) if (contextMenu.CancelOpening())
return; return;

2
src/Avalonia.Controls/DataValidationErrors.cs

@ -56,7 +56,7 @@ namespace Avalonia.Controls
{ {
ErrorsProperty.Changed.Subscribe(ErrorsChanged); ErrorsProperty.Changed.Subscribe(ErrorsChanged);
HasErrorsProperty.Changed.Subscribe(HasErrorsChanged); HasErrorsProperty.Changed.Subscribe(HasErrorsChanged);
TemplatedParentProperty.Changed.AddClassHandler<DataValidationErrors>(x => x.OnTemplatedParentChange); TemplatedParentProperty.Changed.AddClassHandler<DataValidationErrors>((x, e) => x.OnTemplatedParentChange(e));
} }
private void OnTemplatedParentChange(AvaloniaPropertyChangedEventArgs e) private void OnTemplatedParentChange(AvaloniaPropertyChangedEventArgs e)

2
src/Avalonia.Controls/Decorator.cs

@ -29,7 +29,7 @@ namespace Avalonia.Controls
static Decorator() static Decorator()
{ {
AffectsMeasure<Decorator>(ChildProperty, PaddingProperty); AffectsMeasure<Decorator>(ChildProperty, PaddingProperty);
ChildProperty.Changed.AddClassHandler<Decorator>(x => x.ChildChanged); ChildProperty.Changed.AddClassHandler<Decorator>((x, e) => x.ChildChanged(e));
} }
/// <summary> /// <summary>

4
src/Avalonia.Controls/DropDown.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
{ {
public DropDown() public DropDown()
{ {
Logger.Warning(LogArea.Control, this, "DropDown is deprecated: Use ComboBox"); Logger.TryGet(LogEventLevel.Warning)?.Log(LogArea.Control, this, "DropDown is deprecated: Use ComboBox");
} }
Type IStyleable.StyleKey => typeof(ComboBox); Type IStyleable.StyleKey => typeof(ComboBox);
@ -20,7 +20,7 @@ namespace Avalonia.Controls
{ {
public DropDownItem() public DropDownItem()
{ {
Logger.Warning(LogArea.Control, this, "DropDownItem is deprecated: Use ComboBoxItem"); Logger.TryGet(LogEventLevel.Warning)?.Log(LogArea.Control, this, "DropDownItem is deprecated: Use ComboBoxItem");
} }
Type IStyleable.StyleKey => typeof(ComboBoxItem); Type IStyleable.StyleKey => typeof(ComboBoxItem);

2
src/Avalonia.Controls/Expander.cs

@ -37,7 +37,7 @@ namespace Avalonia.Controls
PseudoClass<Expander>(IsExpandedProperty, ":expanded"); PseudoClass<Expander>(IsExpandedProperty, ":expanded");
IsExpandedProperty.Changed.AddClassHandler<Expander>(x => x.OnIsExpandedChanged); IsExpandedProperty.Changed.AddClassHandler<Expander>((x, e) => x.OnIsExpandedChanged(e));
} }
public IPageTransition ContentTransition public IPageTransition ContentTransition

11
src/Avalonia.Controls/Grid.cs

@ -24,13 +24,14 @@ namespace Avalonia.Controls
{ {
static Grid() static Grid()
{ {
IsSharedSizeScopeProperty.Changed.AddClassHandler<Control>(DefinitionBase.OnIsSharedSizeScopePropertyChanged);
ShowGridLinesProperty.Changed.AddClassHandler<Grid>(OnShowGridLinesPropertyChanged); ShowGridLinesProperty.Changed.AddClassHandler<Grid>(OnShowGridLinesPropertyChanged);
ColumnProperty.Changed.AddClassHandler<Grid>(OnCellAttachedPropertyChanged); IsSharedSizeScopeProperty.Changed.AddClassHandler<Control>(DefinitionBase.OnIsSharedSizeScopePropertyChanged);
ColumnSpanProperty.Changed.AddClassHandler<Grid>(OnCellAttachedPropertyChanged); ColumnProperty.Changed.AddClassHandler<Control>(OnCellAttachedPropertyChanged);
RowProperty.Changed.AddClassHandler<Grid>(OnCellAttachedPropertyChanged); ColumnSpanProperty.Changed.AddClassHandler<Control>(OnCellAttachedPropertyChanged);
RowSpanProperty.Changed.AddClassHandler<Grid>(OnCellAttachedPropertyChanged); RowProperty.Changed.AddClassHandler<Control>(OnCellAttachedPropertyChanged);
RowSpanProperty.Changed.AddClassHandler<Control>(OnCellAttachedPropertyChanged);
AffectsParentMeasure<Grid>(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty); AffectsParentMeasure<Grid>(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty);
} }

12
src/Avalonia.Controls/ItemsControl.cs

@ -64,8 +64,8 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static ItemsControl() static ItemsControl()
{ {
ItemsProperty.Changed.AddClassHandler<ItemsControl>(x => x.ItemsChanged); ItemsProperty.Changed.AddClassHandler<ItemsControl>((x, e) => x.ItemsChanged(e));
ItemTemplateProperty.Changed.AddClassHandler<ItemsControl>(x => x.ItemTemplateChanged); ItemTemplateProperty.Changed.AddClassHandler<ItemsControl>((x, e) => x.ItemTemplateChanged(e));
} }
/// <summary> /// <summary>
@ -489,18 +489,20 @@ namespace Avalonia.Controls
bool wrap) bool wrap)
{ {
IInputElement result; IInputElement result;
var c = from;
do do
{ {
result = container.GetControl(direction, from, wrap); result = container.GetControl(direction, c, wrap);
from = from ?? result;
if (result?.Focusable == true) if (result?.Focusable == true)
{ {
return result; return result;
} }
from = result; c = result;
} while (from != null); } while (c != null && c != from);
return null; return null;
} }

8
src/Avalonia.Controls/LayoutTransformControl.cs

@ -28,11 +28,13 @@ namespace Avalonia.Controls
ClipToBoundsProperty.OverrideDefaultValue<LayoutTransformControl>(true); ClipToBoundsProperty.OverrideDefaultValue<LayoutTransformControl>(true);
LayoutTransformProperty.Changed LayoutTransformProperty.Changed
.AddClassHandler<LayoutTransformControl>(x => x.OnLayoutTransformChanged); .AddClassHandler<LayoutTransformControl>((x, e) => x.OnLayoutTransformChanged(e));
ChildProperty.Changed ChildProperty.Changed
.AddClassHandler<LayoutTransformControl>(x => x.OnChildChanged); .AddClassHandler<LayoutTransformControl>((x, e) => x.OnChildChanged(e));
UseRenderTransformProperty.Changed.AddClassHandler<LayoutTransformControl>(x => x.OnUseRenderTransformPropertyChanged);
UseRenderTransformProperty.Changed
.AddClassHandler<LayoutTransformControl>((x, e) => x.OnUseRenderTransformPropertyChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/MenuBase.cs

@ -64,7 +64,7 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
static MenuBase() static MenuBase()
{ {
MenuItem.SubmenuOpenedEvent.AddClassHandler<MenuBase>(x => x.OnSubmenuOpened); MenuItem.SubmenuOpenedEvent.AddClassHandler<MenuBase>((x, e) => x.OnSubmenuOpened(e));
} }
/// <summary> /// <summary>

16
src/Avalonia.Controls/MenuItem.cs

@ -102,13 +102,13 @@ namespace Avalonia.Controls
SelectableMixin.Attach<MenuItem>(IsSelectedProperty); SelectableMixin.Attach<MenuItem>(IsSelectedProperty);
CommandProperty.Changed.Subscribe(CommandChanged); CommandProperty.Changed.Subscribe(CommandChanged);
FocusableProperty.OverrideDefaultValue<MenuItem>(true); FocusableProperty.OverrideDefaultValue<MenuItem>(true);
HeaderProperty.Changed.AddClassHandler<MenuItem>(x => x.HeaderChanged); HeaderProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.HeaderChanged(e));
IconProperty.Changed.AddClassHandler<MenuItem>(x => x.IconChanged); IconProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.IconChanged(e));
IsSelectedProperty.Changed.AddClassHandler<MenuItem>(x => x.IsSelectedChanged); IsSelectedProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.IsSelectedChanged(e));
ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel);
ClickEvent.AddClassHandler<MenuItem>(x => x.OnClick); ClickEvent.AddClassHandler<MenuItem>((x, e) => x.OnClick(e));
SubmenuOpenedEvent.AddClassHandler<MenuItem>(x => x.OnSubmenuOpened); SubmenuOpenedEvent.AddClassHandler<MenuItem>((x, e) => x.OnSubmenuOpened(e));
IsSubMenuOpenProperty.Changed.AddClassHandler<MenuItem>(x => x.SubMenuOpenChanged); IsSubMenuOpenProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.SubMenuOpenChanged(e));
} }
public MenuItem() public MenuItem()
@ -337,7 +337,7 @@ namespace Avalonia.Controls
{ {
base.OnPointerEnter(e); base.OnPointerEnter(e);
var point = e.GetPointerPoint(null); var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerEnterItemEvent, this, e.Pointer, this.VisualRoot, point.Position, RaiseEvent(new PointerEventArgs(PointerEnterItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
e.Timestamp, point.Properties, e.KeyModifiers)); e.Timestamp, point.Properties, e.KeyModifiers));
} }
@ -347,7 +347,7 @@ namespace Avalonia.Controls
{ {
base.OnPointerLeave(e); base.OnPointerLeave(e);
var point = e.GetPointerPoint(null); var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerLeaveItemEvent, this, e.Pointer, this.VisualRoot, point.Position, RaiseEvent(new PointerEventArgs(PointerLeaveItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
e.Timestamp, point.Properties, e.KeyModifiers)); e.Timestamp, point.Properties, e.KeyModifiers));
} }

85
src/Avalonia.Controls/NativeMenu.Export.cs

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls.Platform;
using Avalonia.Data;
namespace Avalonia.Controls
{
public partial class NativeMenu
{
public static readonly AttachedProperty<bool> IsNativeMenuExportedProperty =
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, bool>("IsNativeMenuExported");
public static bool GetIsNativeMenuExported(TopLevel tl) => tl.GetValue(IsNativeMenuExportedProperty);
private static readonly AttachedProperty<NativeMenuInfo> s_nativeMenuInfoProperty =
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, NativeMenuInfo>("___NativeMenuInfo");
class NativeMenuInfo
{
public bool ChangingIsExported { get; set; }
public ITopLevelNativeMenuExporter Exporter { get; }
public NativeMenuInfo(TopLevel target)
{
Exporter = (target.PlatformImpl as ITopLevelImplWithNativeMenuExporter)?.NativeMenuExporter;
if (Exporter != null)
{
Exporter.OnIsNativeMenuExportedChanged += delegate
{
SetIsNativeMenuExported(target, Exporter.IsNativeMenuExported);
};
}
}
}
static NativeMenuInfo GetInfo(TopLevel target)
{
var rv = target.GetValue(s_nativeMenuInfoProperty);
if (rv == null)
{
target.SetValue(s_nativeMenuInfoProperty, rv = new NativeMenuInfo(target));
SetIsNativeMenuExported(target, rv.Exporter?.IsNativeMenuExported ?? false);
}
return rv;
}
static void SetIsNativeMenuExported(TopLevel tl, bool value)
{
GetInfo(tl).ChangingIsExported = true;
tl.SetValue(IsNativeMenuExportedProperty, value);
}
public static readonly AttachedProperty<NativeMenu> MenuProperty
= AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu>("Menu", validate:
(o, v) =>
{
if(!(o is Application || o is TopLevel))
throw new InvalidOperationException("NativeMenu.Menu property isn't valid on "+o.GetType());
return v;
});
public static void SetMenu(AvaloniaObject o, NativeMenu menu) => o.SetValue(MenuProperty, menu);
public static NativeMenu GetMenu(AvaloniaObject o) => o.GetValue(MenuProperty);
static NativeMenu()
{
// This is needed because of the lack of attached direct properties
IsNativeMenuExportedProperty.Changed.Subscribe(args =>
{
var info = GetInfo((TopLevel)args.Sender);
if (!info.ChangingIsExported)
throw new InvalidOperationException("IsNativeMenuExported property is read-only");
info.ChangingIsExported = false;
});
MenuProperty.Changed.Subscribe(args =>
{
if (args.Sender is TopLevel tl)
{
GetInfo(tl).Exporter?.SetNativeMenu((NativeMenu)args.NewValue);
}
});
}
}
}

60
src/Avalonia.Controls/NativeMenu.cs

@ -0,0 +1,60 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Data;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
namespace Avalonia.Controls
{
public partial class NativeMenu : AvaloniaObject, IEnumerable<NativeMenuItemBase>
{
private readonly AvaloniaList<NativeMenuItemBase> _items =
new AvaloniaList<NativeMenuItemBase> { ResetBehavior = ResetBehavior.Remove };
private NativeMenuItem _parent;
[Content]
public IList<NativeMenuItemBase> Items => _items;
public NativeMenu()
{
_items.Validate = Validator;
_items.CollectionChanged += ItemsChanged;
}
private void Validator(NativeMenuItemBase obj)
{
if (obj.Parent != null)
throw new InvalidOperationException("NativeMenuItem already has a parent");
}
private void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if(e.OldItems!=null)
foreach (NativeMenuItemBase i in e.OldItems)
i.Parent = null;
if(e.NewItems!=null)
foreach (NativeMenuItemBase i in e.NewItems)
i.Parent = this;
}
public static readonly DirectProperty<NativeMenu, NativeMenuItem> ParentProperty =
AvaloniaProperty.RegisterDirect<NativeMenu, NativeMenuItem>("Parent", o => o.Parent, (o, v) => o.Parent = v);
public NativeMenuItem Parent
{
get => _parent;
set => SetAndRaise(ParentProperty, ref _parent, value);
}
public void Add(NativeMenuItemBase item) => _items.Add(item);
public IEnumerator<NativeMenuItemBase> GetEnumerator() => _items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

36
src/Avalonia.Controls/NativeMenuBar.cs

@ -0,0 +1,36 @@
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Styling;
namespace Avalonia.Controls
{
public class NativeMenuBar : TemplatedControl
{
public static readonly AttachedProperty<bool> EnableMenuItemClickForwardingProperty =
AvaloniaProperty.RegisterAttached<NativeMenuBar, MenuItem, Boolean>(
"EnableMenuItemClickForwarding");
static NativeMenuBar()
{
EnableMenuItemClickForwardingProperty.Changed.Subscribe(args =>
{
var item = (MenuItem)args.Sender;
if (args.NewValue.Equals(true))
item.Click += OnMenuItemClick;
else
item.Click -= OnMenuItemClick;
});
}
public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable)
{
menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable);
}
private static void OnMenuItemClick(object sender, RoutedEventArgs e)
{
(((MenuItem)sender).DataContext as NativeMenuItem)?.RaiseClick();
}
}
}

162
src/Avalonia.Controls/NativeMenuItem.cs

@ -0,0 +1,162 @@
using System;
using System.Windows.Input;
using Avalonia.Input;
using Avalonia.Utilities;
namespace Avalonia.Controls
{
public class NativeMenuItem : NativeMenuItemBase
{
private string _header;
private KeyGesture _gesture;
private bool _enabled = true;
private NativeMenu _menu;
static NativeMenuItem()
{
MenuProperty.Changed.Subscribe(args =>
{
var item = (NativeMenuItem)args.Sender;
var value = (NativeMenu)args.NewValue;
if (value.Parent != null && value.Parent != item)
throw new InvalidOperationException("NativeMenu already has a parent");
value.Parent = item;
});
}
class CanExecuteChangedSubscriber : IWeakSubscriber<EventArgs>
{
private readonly NativeMenuItem _parent;
public CanExecuteChangedSubscriber(NativeMenuItem parent)
{
_parent = parent;
}
public void OnEvent(object sender, EventArgs e)
{
_parent.CanExecuteChanged();
}
}
private readonly CanExecuteChangedSubscriber _canExecuteChangedSubscriber;
public NativeMenuItem()
{
_canExecuteChangedSubscriber = new CanExecuteChangedSubscriber(this);
}
public NativeMenuItem(string header) : this()
{
Header = header;
}
public static readonly DirectProperty<NativeMenuItem, NativeMenu> MenuProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>(nameof(Menu), o => o._menu,
(o, v) =>
{
if (v.Parent != null && v.Parent != o)
throw new InvalidOperationException("NativeMenu already has a parent");
o._menu = v;
});
public NativeMenu Menu
{
get => _menu;
set
{
if (value.Parent != null && value.Parent != this)
throw new InvalidOperationException("NativeMenu already has a parent");
SetAndRaise(MenuProperty, ref _menu, value);
}
}
public static readonly DirectProperty<NativeMenuItem, string> HeaderProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, string>(nameof(Header), o => o._header, (o, v) => o._header = v);
public string Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public static readonly DirectProperty<NativeMenuItem, KeyGesture> GestureProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, KeyGesture>(nameof(Gesture), o => o._gesture, (o, v) => o._gesture = v);
public KeyGesture Gesture
{
get => GetValue(GestureProperty);
set => SetValue(GestureProperty, value);
}
private ICommand _command;
public static readonly DirectProperty<NativeMenuItem, ICommand> CommandProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, ICommand>(nameof(Command),
o => o._command, (o, v) =>
{
if (o._command != null)
WeakSubscriptionManager.Unsubscribe(o._command,
nameof(ICommand.CanExecuteChanged), o._canExecuteChangedSubscriber);
o._command = v;
if (o._command != null)
WeakSubscriptionManager.Subscribe(o._command,
nameof(ICommand.CanExecuteChanged), o._canExecuteChangedSubscriber);
o.CanExecuteChanged();
});
/// <summary>
/// Defines the <see cref="CommandParameter"/> property.
/// </summary>
public static readonly StyledProperty<object> CommandParameterProperty =
Button.CommandParameterProperty.AddOwner<MenuItem>();
public static readonly DirectProperty<NativeMenuItem, bool> EnabledProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, bool>(nameof(Enabled), o => o._enabled,
(o, v) => o._enabled = v, true);
public bool Enabled
{
get => GetValue(EnabledProperty);
set => SetValue(EnabledProperty, value);
}
void CanExecuteChanged()
{
Enabled = _command?.CanExecute(null) ?? true;
}
public bool HasClickHandlers => Clicked != null;
public ICommand Command
{
get => GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
/// <summary>
/// Gets or sets the parameter to pass to the <see cref="Command"/> property of a
/// <see cref="NativeMenuItem"/>.
/// </summary>
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public event EventHandler Clicked;
public void RaiseClick()
{
Clicked?.Invoke(this, new EventArgs());
if (Command?.CanExecute(CommandParameter) == true)
{
Command.Execute(CommandParameter);
}
}
}
}

23
src/Avalonia.Controls/NativeMenuItemBase.cs

@ -0,0 +1,23 @@
using System;
namespace Avalonia.Controls
{
public class NativeMenuItemBase : AvaloniaObject
{
private NativeMenu _parent;
internal NativeMenuItemBase()
{
}
public static readonly DirectProperty<NativeMenuItem, NativeMenu> ParentProperty =
AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>("Parent", o => o.Parent, (o, v) => o.Parent = v);
public NativeMenu Parent
{
get => _parent;
set => SetAndRaise(ParentProperty, ref _parent, value);
}
}
}

10
src/Avalonia.Controls/NativeMenuItemSeperator.cs

@ -0,0 +1,10 @@
using System;
namespace Avalonia.Controls
{
public class NativeMenuItemSeperator : NativeMenuItemBase
{
[Obsolete("This is a temporary hack to make our MenuItem recognize this as a separator, don't use", true)]
public string Header => "-";
}
}

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

@ -273,7 +273,8 @@ namespace Avalonia.Controls.Platform
if (item.IsTopLevel) if (item.IsTopLevel)
{ {
if (item.Parent.SelectedItem?.IsSubMenuOpen == true) if (item != item.Parent.SelectedItem &&
item.Parent.SelectedItem?.IsSubMenuOpen == true)
{ {
item.Parent.SelectedItem.Close(); item.Parent.SelectedItem.Close();
SelectItemAndAncestors(item); SelectItemAndAncestors(item);
@ -356,7 +357,7 @@ namespace Avalonia.Controls.Platform
{ {
var item = GetMenuItem(e.Source as IControl); var item = GetMenuItem(e.Source as IControl);
if (e.MouseButton == MouseButton.Left && item?.HasSubMenu == false) if (e.InitialPressMouseButton == MouseButton.Left && item?.HasSubMenu == false)
{ {
Click(item); Click(item);
e.Handled = true; e.Handled = true;

18
src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using Avalonia.Platform;
namespace Avalonia.Controls.Platform
{
public interface ITopLevelNativeMenuExporter
{
bool IsNativeMenuExported { get; }
event EventHandler OnIsNativeMenuExportedChanged;
void SetNativeMenu(NativeMenu menu);
}
public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl
{
ITopLevelNativeMenuExporter NativeMenuExporter { get; }
}
}

13
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -3,6 +3,7 @@
using System; using System;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input;
namespace Avalonia.Platform namespace Avalonia.Platform
{ {
@ -57,28 +58,28 @@ namespace Avalonia.Platform
/// Return true to prevent the underlying implementation from closing. /// Return true to prevent the underlying implementation from closing.
/// </summary> /// </summary>
Func<bool> Closing { get; set; } Func<bool> Closing { get; set; }
/// <summary> /// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler. /// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary> /// </summary>
void BeginMoveDrag(); void BeginMoveDrag(PointerPressedEventArgs e);
/// <summary> /// <summary>
/// Starts resizing a window. This function is used if an application has window resizing controls. /// Starts resizing a window. This function is used if an application has window resizing controls.
/// Should be called from left mouse button press event handler /// Should be called from left mouse button press event handler
/// </summary> /// </summary>
void BeginResizeDrag(WindowEdge edge); void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e);
/// <summary> /// <summary>
/// Sets the client size of the top level. /// Sets the client size of the top level.
/// </summary> /// </summary>
void Resize(Size clientSize); void Resize(Size clientSize);
/// <summary> /// <summary>
/// Sets the client size of the top level. /// Sets the client size of the top level.
/// </summary> /// </summary>
void Move(PixelPoint point); void Move(PixelPoint point);
/// <summary> /// <summary>
/// Minimum width of the window. /// Minimum width of the window.
/// </summary> /// </summary>

6
src/Avalonia.Controls/Platform/InProcessDragSource.cs

@ -21,7 +21,7 @@ namespace Avalonia.Platform
private DragDropEffects _allowedEffects; private DragDropEffects _allowedEffects;
private IDataObject _draggedData; private IDataObject _draggedData;
private IInputElement _lastRoot; private IInputRoot _lastRoot;
private Point _lastPosition; private Point _lastPosition;
private StandardCursorType _lastCursorType; private StandardCursorType _lastCursorType;
private object _originalCursor; private object _originalCursor;
@ -56,7 +56,7 @@ namespace Avalonia.Platform
return DragDropEffects.None; return DragDropEffects.None;
} }
private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputElement root, Point pt, RawInputModifiers modifiers) private DragDropEffects RaiseEventAndUpdateCursor(RawDragEventType type, IInputRoot root, Point pt, RawInputModifiers modifiers)
{ {
_lastPosition = pt; _lastPosition = pt;
@ -91,7 +91,7 @@ namespace Avalonia.Platform
return StandardCursorType.No; return StandardCursorType.No;
} }
private void UpdateCursor(IInputElement root, DragDropEffects effect) private void UpdateCursor(IInputRoot root, DragDropEffects effect)
{ {
if (_lastRoot != root) if (_lastRoot != root)
{ {

4
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@ -46,8 +46,8 @@ namespace Avalonia.Controls.Presenters
/// </summary> /// </summary>
static CarouselPresenter() static CarouselPresenter()
{ {
IsVirtualizedProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.IsVirtualizedChanged); IsVirtualizedProperty.Changed.AddClassHandler<CarouselPresenter>((x, e) => x.IsVirtualizedChanged(e));
SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.SelectedIndexChanged); SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>((x, e) => x.SelectedIndexChanged(e));
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -94,9 +94,9 @@ namespace Avalonia.Controls.Presenters
{ {
AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty); AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty);
ContentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>(x => x.ContentChanged); ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>(x => x.TemplatedParentChanged); TemplatedParentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.TemplatedParentChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -35,7 +35,7 @@ namespace Avalonia.Controls.Presenters
KeyboardNavigationMode.Once); KeyboardNavigationMode.Once);
VirtualizationModeProperty.Changed VirtualizationModeProperty.Changed
.AddClassHandler<ItemsPresenter>(x => x.VirtualizationModeChanged); .AddClassHandler<ItemsPresenter>((x, e) => x.VirtualizationModeChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -45,7 +45,7 @@ namespace Avalonia.Controls.Presenters
/// </summary> /// </summary>
static ItemsPresenterBase() static ItemsPresenterBase()
{ {
TemplatedParentProperty.Changed.AddClassHandler<ItemsPresenterBase>(x => x.TemplatedParentChanged); TemplatedParentProperty.Changed.AddClassHandler<ItemsPresenterBase>((x,e) => x.TemplatedParentChanged(e));
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -73,7 +73,7 @@ namespace Avalonia.Controls.Presenters
static ScrollContentPresenter() static ScrollContentPresenter()
{ {
ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true); ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>(x => x.ChildChanged); ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>((x, e) => x.ChildChanged(e));
AffectsArrange<ScrollContentPresenter>(OffsetProperty); AffectsArrange<ScrollContentPresenter>(OffsetProperty);
} }
@ -246,7 +246,7 @@ namespace Avalonia.Controls.Presenters
if (isLogical) if (isLogical)
_activeLogicalGestureScrolls?.TryGetValue(e.Id, out delta); _activeLogicalGestureScrolls?.TryGetValue(e.Id, out delta);
delta += e.Delta; delta += e.Delta;
if (Extent.Height > Viewport.Height) if (Extent.Height > Viewport.Height)
{ {
double dy; double dy;
@ -293,7 +293,7 @@ namespace Avalonia.Controls.Presenters
} }
} }
private void OnScrollGestureEnded(object sender, ScrollGestureEndedEventArgs e) private void OnScrollGestureEnded(object sender, ScrollGestureEndedEventArgs e)
=> _activeLogicalGestureScrolls?.Remove(e.Id); => _activeLogicalGestureScrolls?.Remove(e.Id);
/// <inheritdoc/> /// <inheritdoc/>

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -244,7 +244,7 @@ namespace Avalonia.Controls.Presenters
var rect = FormattedText.HitTestTextPosition(caretIndex); var rect = FormattedText.HitTestTextPosition(caretIndex);
this.BringIntoView(rect); this.BringIntoView(rect);
}, },
DispatcherPriority.Normal); DispatcherPriority.Render);
} }
} }
} }

2
src/Avalonia.Controls/Primitives/HeaderedContentControl.cs

@ -30,7 +30,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static HeaderedContentControl() static HeaderedContentControl()
{ {
ContentProperty.Changed.AddClassHandler<HeaderedContentControl>(x => x.HeaderChanged); ContentProperty.Changed.AddClassHandler<HeaderedContentControl>((x, e) => x.HeaderChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static HeaderedItemsControl() static HeaderedItemsControl()
{ {
HeaderProperty.Changed.AddClassHandler<HeaderedItemsControl>(x => x.HeaderChanged); HeaderProperty.Changed.AddClassHandler<HeaderedItemsControl>((x, e) => x.HeaderChanged(e));
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static HeaderedSelectingItemsControl() static HeaderedSelectingItemsControl()
{ {
HeaderProperty.Changed.AddClassHandler<HeaderedSelectingItemsControl>(x => x.HeaderChanged); HeaderProperty.Changed.AddClassHandler<HeaderedSelectingItemsControl>((x, e) => x.HeaderChanged(e));
} }
/// <summary> /// <summary>

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

@ -93,8 +93,8 @@ namespace Avalonia.Controls.Primitives
static Popup() static Popup()
{ {
IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false); IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false);
ChildProperty.Changed.AddClassHandler<Popup>(x => x.ChildChanged); ChildProperty.Changed.AddClassHandler<Popup>((x, e) => x.ChildChanged(e));
IsOpenProperty.Changed.AddClassHandler<Popup>(x => x.IsOpenChanged); IsOpenProperty.Changed.AddClassHandler<Popup>((x, e) => x.IsOpenChanged(e));
} }
public Popup() public Popup()

2
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives
/// <summary> /// <summary>
/// The root window of a <see cref="Popup"/>. /// The root window of a <see cref="Popup"/>.
/// </summary> /// </summary>
public class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost
{ {
private readonly TopLevel _parent; private readonly TopLevel _parent;
private PopupPositionerParameters _positionerParameters; private PopupPositionerParameters _positionerParameters;

4
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -58,8 +58,8 @@ namespace Avalonia.Controls.Primitives
PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical"); PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); PseudoClass<ScrollBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble);
} }
/// <summary> /// <summary>

8
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives
/// </summary> /// </summary>
static SelectingItemsControl() static SelectingItemsControl()
{ {
IsSelectedChangedEvent.AddClassHandler<SelectingItemsControl>(x => x.ContainerSelectionChanged); IsSelectedChangedEvent.AddClassHandler<SelectingItemsControl>((x, e) => x.ContainerSelectionChanged(e));
} }
/// <summary> /// <summary>
@ -1062,7 +1062,7 @@ namespace Avalonia.Controls.Primitives
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Error( Logger.TryGet(LogEventLevel.Error)?.Log(
LogArea.Property, LogArea.Property,
this, this,
"Error thrown updating SelectedItems: {Error}", "Error thrown updating SelectedItems: {Error}",
@ -1088,8 +1088,8 @@ namespace Avalonia.Controls.Primitives
} }
else else
{ {
SelectedIndex = _updateSelectedIndex != int.MinValue ? SelectedIndex = _updateSelectedIndex != int.MinValue ?
_updateSelectedIndex : _updateSelectedIndex :
AlwaysSelected ? 0 : -1; AlwaysSelected ? 0 : -1;
} }
} }

4
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives
static TemplatedControl() static TemplatedControl()
{ {
ClipToBoundsProperty.OverrideDefaultValue<TemplatedControl>(true); ClipToBoundsProperty.OverrideDefaultValue<TemplatedControl>(true);
TemplateProperty.Changed.AddClassHandler<TemplatedControl>(x => x.OnTemplateChanged); TemplateProperty.Changed.AddClassHandler<TemplatedControl>((x, e) => x.OnTemplateChanged(e));
} }
/// <summary> /// <summary>
@ -255,7 +255,7 @@ namespace Avalonia.Controls.Primitives
if (template != null) if (template != null)
{ {
Logger.Verbose(LogArea.Control, this, "Creating control template"); Logger.TryGet(LogEventLevel.Verbose)?.Log(LogArea.Control, this, "Creating control template");
var (child, nameScope) = template.Build(this); var (child, nameScope) = template.Build(this);
ApplyTemplatedParent(child); ApplyTemplatedParent(child);

10
src/Avalonia.Controls/Primitives/Thumb.cs

@ -22,9 +22,9 @@ namespace Avalonia.Controls.Primitives
static Thumb() static Thumb()
{ {
DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted, RoutingStrategies.Bubble); DragStartedEvent.AddClassHandler<Thumb>((x,e) => x.OnDragStarted(e), RoutingStrategies.Bubble);
DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta, RoutingStrategies.Bubble); DragDeltaEvent.AddClassHandler<Thumb>((x, e) => x.OnDragDelta(e), RoutingStrategies.Bubble);
DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted, RoutingStrategies.Bubble); DragCompletedEvent.AddClassHandler<Thumb>((x, e) => x.OnDragCompleted(e), RoutingStrategies.Bubble);
} }
public event EventHandler<VectorEventArgs> DragStarted public event EventHandler<VectorEventArgs> DragStarted
@ -83,6 +83,8 @@ namespace Avalonia.Controls.Primitives
Vector = (Vector)_lastPoint, Vector = (Vector)_lastPoint,
}; };
PseudoClasses.Add(":pressed");
RaiseEvent(ev); RaiseEvent(ev);
} }
@ -102,6 +104,8 @@ namespace Avalonia.Controls.Primitives
RaiseEvent(ev); RaiseEvent(ev);
} }
PseudoClasses.Remove(":pressed");
} }
} }
} }

6
src/Avalonia.Controls/Primitives/Track.cs

@ -48,9 +48,9 @@ namespace Avalonia.Controls.Primitives
{ {
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical"); PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); PseudoClass<Track, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged); ThumbProperty.Changed.AddClassHandler<Track>((x,e) => x.ThumbChanged(e));
IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged); IncreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged); DecreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
AffectsArrange<Track>(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty); AffectsArrange<Track>(MinimumProperty, MaximumProperty, ValueProperty, OrientationProperty);
} }

5
src/Avalonia.Controls/Primitives/UniformGrid.cs

@ -28,6 +28,11 @@ namespace Avalonia.Controls.Primitives
private int _rows; private int _rows;
private int _columns; private int _columns;
static UniformGrid()
{
AffectsMeasure<UniformGrid>(RowsProperty, ColumnsProperty, FirstColumnProperty);
}
/// <summary> /// <summary>
/// Specifies the row count. If set to 0, row count will be calculated automatically. /// Specifies the row count. If set to 0, row count will be calculated automatically.
/// </summary> /// </summary>

5
src/Avalonia.Controls/ProgressBar.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Layout; using Avalonia.Layout;
@ -38,8 +39,8 @@ namespace Avalonia.Controls
PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate"); PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged); ValueProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged); IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
} }
public bool IsIndeterminate public bool IsIndeterminate

2
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -227,6 +227,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawKeyEventArgs( Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,
InputRoot,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key, (Key)key.Key,
GetAvaloniaRawInputModifiers(key.Modifiers))); GetAvaloniaRawInputModifiers(key.Modifiers)));
@ -241,6 +242,7 @@ namespace Avalonia.Controls.Remote.Server
Input?.Invoke(new RawTextInputEventArgs( Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice, KeyboardDevice,
0, 0,
InputRoot,
text.Text)); text.Text));
}, DispatcherPriority.Input); }, DispatcherPriority.Input);
} }

4
src/Avalonia.Controls/RepeatButton.cs

@ -98,10 +98,10 @@ namespace Avalonia.Controls
{ {
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (e.MouseButton == MouseButton.Left) if (e.InitialPressMouseButton == MouseButton.Left)
{ {
StopTimer(); StopTimer();
} }
} }
} }
} }

4
src/Avalonia.Controls/ScrollViewer.cs

@ -163,8 +163,8 @@ namespace Avalonia.Controls
{ {
AffectsValidation(ExtentProperty, OffsetProperty); AffectsValidation(ExtentProperty, OffsetProperty);
AffectsValidation(ViewportProperty, OffsetProperty); AffectsValidation(ViewportProperty, OffsetProperty);
HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged); HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>((x, e) => x.ScrollBarVisibilityChanged(e));
VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged); VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>((x, e) => x.ScrollBarVisibilityChanged(e));
} }
/// <summary> /// <summary>

6
src/Avalonia.Controls/Slider.cs

@ -45,9 +45,9 @@ namespace Avalonia.Controls
OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal); OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical"); PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal"); PseudoClass<Slider, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragStartedEvent.AddClassHandler<Slider>(x => x.OnThumbDragStarted, RoutingStrategies.Bubble); Thumb.DragStartedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<Slider>(x => x.OnThumbDragDelta, RoutingStrategies.Bubble); Thumb.DragDeltaEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<Slider>(x => x.OnThumbDragCompleted, RoutingStrategies.Bubble); Thumb.DragCompletedEvent.AddClassHandler<Slider>((x, e) => x.OnThumbDragCompleted(e), RoutingStrategies.Bubble);
} }
/// <summary> /// <summary>

2
src/Avalonia.Controls/TabControl.cs

@ -196,7 +196,7 @@ namespace Avalonia.Controls
protected override void OnPointerReleased(PointerReleasedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
if (e.MouseButton == MouseButton.Left && e.Pointer.Type != PointerType.Mouse) if (e.InitialPressMouseButton == MouseButton.Left && e.Pointer.Type != PointerType.Mouse)
{ {
var container = GetContainerFromEventSource(e.Source); var container = GetContainerFromEventSource(e.Source);
if (container != null if (container != null

4
src/Avalonia.Controls/TabItem.cs

@ -30,8 +30,8 @@ namespace Avalonia.Controls
{ {
SelectableMixin.Attach<TabItem>(IsSelectedProperty); SelectableMixin.Attach<TabItem>(IsSelectedProperty);
FocusableProperty.OverrideDefaultValue(typeof(TabItem), true); FocusableProperty.OverrideDefaultValue(typeof(TabItem), true);
IsSelectedProperty.Changed.AddClassHandler<TabItem>(x => x.UpdateSelectedContent); IsSelectedProperty.Changed.AddClassHandler<TabItem>((x, e) => x.UpdateSelectedContent(e));
DataContextProperty.Changed.AddClassHandler<TabItem>(x => x.UpdateHeader); DataContextProperty.Changed.AddClassHandler<TabItem>((x, e) => x.UpdateHeader(e));
} }
/// <summary> /// <summary>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save