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/=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/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/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>

1
build/Base.props

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

1
build/BuildTargets.targets

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

2
global.json

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

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

@ -22,6 +22,8 @@ struct IAvnGlContext;
struct IAvnGlDisplay;
struct IAvnGlSurfaceRenderTarget;
struct IAvnGlSurfaceRenderingSession;
struct IAvnAppMenu;
struct IAvnAppMenuItem;
struct AvnSize
{
@ -173,6 +175,11 @@ public:
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
virtual HRESULT CreateCursorFactory(IAvnCursorFactory** 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
@ -203,6 +210,8 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT SetCursor(IAvnCursor* cursor) = 0;
virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** 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 void Unlock() = 0;
};
@ -258,6 +267,7 @@ AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
AVNCOM(IAvnMacOptions, 07) : IUnknown
{
virtual HRESULT SetShowInDock(int show) = 0;
virtual HRESULT SetApplicationTitle (void* utf8string) = 0;
};
AVNCOM(IAvnActionCallback, 08) : IUnknown
@ -367,4 +377,25 @@ AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown
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();

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

@ -8,10 +8,12 @@
/* Begin PBXBuildFile section */
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 */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37DDA9AF219330F8002E132B /* AvnString.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 */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
@ -24,6 +26,7 @@
/* Begin PBXFileReference section */
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>"; };
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>"; };
@ -32,6 +35,7 @@
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>"; };
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>"; };
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>"; };
@ -85,6 +89,8 @@
AB661C1F2148286E00291242 /* window.mm */,
37C09D8A21581EF2006A6758 /* window.h */,
AB00E4F62147CA920032A60A /* main.mm */,
37155CE3233C00EB0034DCE9 /* menu.h */,
520624B222973F4100C4DCEF /* menu.mm */,
37A517B22159597E00FBA241 /* Screens.mm */,
37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
AB7A61F02147C815003C5833 /* Products */,
@ -107,6 +113,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -150,6 +157,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = AB7A61E62147C814003C5833;
@ -173,6 +181,7 @@
37DDA9B0219330F8002E132B /* AvnString.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
AB00E4F72147CA920032A60A /* main.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 IAvnGlFeature* GetGlFeature();
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 NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
@ -40,4 +47,9 @@ template<typename T> inline T* objc_cast(id from) {
return nil;
}
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@end
#endif

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

@ -5,10 +5,121 @@
#define COM_GUIDS_MATERIALIZE
#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>
{
public:
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
{
AvnDesiredActivationPolicy = show
@ -17,8 +128,6 @@ public:
}
};
/// 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
@interface ThreadingInitializer : NSObject
@ -43,8 +152,6 @@ public:
close(_fds[0]);
close(_fds[1]);
}
@end
@ -123,6 +230,42 @@ public:
*ppv = rv;
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()

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;
@end
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@end
@implementation ActionCallback
{
ComPtr<IAvnActionCallback> _callback;

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

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

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

@ -5,6 +5,7 @@
#include "window.h"
#include "KeyTransform.h"
#include "cursor.h"
#include "menu.h"
#include <OpenGL/gl.h>
class SoftwareDrawingOperation
@ -63,9 +64,11 @@ public:
SoftwareDrawingOperation CurrentSwDrawingOperation;
AvnPoint lastPositionSet;
NSString* _lastTitle;
IAvnAppMenu* _mainMenu;
WindowBaseImpl(IAvnWindowBaseEvents* events)
{
_mainMenu = nullptr;
BaseEvents = events;
View = [[AvnView alloc] initWithParent:this];
@ -93,6 +96,7 @@ public:
UpdateStyle();
[Window makeKeyAndOrderFront:Window];
[NSApp activateIgnoringOtherApps:YES];
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
@ -122,6 +126,7 @@ public:
if(Window != nullptr)
{
[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
{
@autoreleasepool
@ -825,8 +855,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
if(type == Wheel)
{
delta.X = [event scrollingDeltaX] / 5;
delta.Y = [event scrollingDeltaY] / 5;
delta.X = [event scrollingDeltaX] / 50;
delta.Y = [event scrollingDeltaY] / 50;
if(delta.X == 0 && delta.Y == 0)
{
@ -1042,6 +1072,19 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
ComPtr<WindowBaseImpl> _parent;
bool _canBecomeKeyAndMain;
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
@ -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
{
_canBecomeKeyAndMain = true;
@ -1157,6 +1226,24 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
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();
[super becomeKeyWindow];
}
@ -1201,6 +1288,28 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
{
if(_parent)
_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];
}

2
packages/Avalonia/AvaloniaBuildTasks.targets

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

8
readme.md

@ -8,9 +8,9 @@
## 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 |
|---|---|---|
@ -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!
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))
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.Desktop

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

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

2
samples/ControlCatalog.Android/MainActivity.cs

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

6
samples/ControlCatalog.NetCore/Program.cs

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

18
samples/ControlCatalog/App.xaml

@ -2,9 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App">
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="FontWeight" Value="Medium"/>
@ -17,4 +17,16 @@
</Style>
<StyleInclude Source="/SideBar.xaml"/>
</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>

13
samples/ControlCatalog/App.xaml.cs

@ -1,4 +1,6 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
@ -6,9 +8,20 @@ namespace ControlCatalog
{
public class App : Application
{
private NativeMenu _recentMenu;
public override void Initialize()
{
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()

25
samples/ControlCatalog/DecoratedWindow.xaml

@ -3,6 +3,31 @@
x:Class="ControlCatalog.DecoratedWindow"
Title="Avalonia Control Gallery"
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">
<DockPanel Grid.Column="1" Grid.Row="1" >
<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);
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += delegate
ctl.PointerPressed += (i, e) =>
{
PlatformImpl?.BeginResizeDrag(edge);
PlatformImpl?.BeginResizeDrag(edge, e);
};
}
private void InitializeComponent()
{
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("Right", StandardCursorType.RightSide, WindowEdge.East);

31
samples/ControlCatalog/MainWindow.xaml

@ -8,7 +8,36 @@
xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views"
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">
<v:CustomNotificationView />
</DataTemplate>

14
samples/ControlCatalog/MainWindow.xaml.cs

@ -14,6 +14,7 @@ namespace ControlCatalog
public class MainWindow : Window
{
private WindowNotificationManager _notificationArea;
private NativeMenu _recentMenu;
public MainWindow()
{
@ -29,8 +30,21 @@ namespace ControlCatalog
};
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()
{
// TODO: iOS does not support dynamically loading assemblies

5
samples/ControlCatalog/Pages/MenuPage.xaml

@ -3,8 +3,11 @@
x:Class="ControlCatalog.Pages.MenuPage">
<StackPanel Orientation="Vertical" Spacing="4">
<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>
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
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()
{
builder.UseWindowingSubsystem(() => Android.AndroidPlatform.Initialize(builder.Instance), "Android");
builder.UseWindowingSubsystem(() => Android.AndroidPlatform.Initialize(builder.ApplicationType), "Android");
builder.UseSkia();
return builder;
}
@ -41,7 +41,7 @@ namespace Avalonia.Android
_scalingFactor = global::Android.App.Application.Context.Resources.DisplayMetrics.ScaledDensity;
}
public static void Initialize(Avalonia.Application app)
public static void Initialize(Type appType)
{
AvaloniaLocator.CurrentMutable
.Bind<IClipboard>().ToTransient<ClipboardImpl>()
@ -55,7 +55,7 @@ namespace Avalonia.Android
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
.Bind<IAssetLoader>().ToConstant(new AssetLoader(appType.Assembly));
SkiaPlatform.Initialize();
((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">
<PropertyGroup>
<TargetFramework>monoandroid80</TargetFramework>
<TargetFramework>monoandroid90</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>

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

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

23
src/Avalonia.Base/AvaloniaObject.cs

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

9
src/Avalonia.Base/AvaloniaObjectExtensions.cs

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

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

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

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

@ -8,6 +8,77 @@ namespace Avalonia.Logging
/// </summary>
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>
/// Logs a new event.
/// </summary>

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

@ -1,8 +1,6 @@
// 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>
@ -16,124 +14,43 @@ namespace Avalonia.Logging
public static ILogSink Sink { get; set; }
/// <summary>
/// Logs an event.
/// Checks if given log level is enabled.
/// </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="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)
/// <returns><see langword="true"/> if given log level is enabled.</returns>
public static bool IsEnabled(LogEventLevel level)
{
Log(LogEventLevel.Verbose, area, source, messageTemplate, propertyValues);
return Sink?.IsEnabled(level) == true;
}
/// <summary>
/// Logs an event with the <see cref="LogEventLevel.Debug"/> level.
/// Returns parametrized logging sink if given log level is enabled.
/// </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 Debug(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
/// <param name="level">The log event level.</param>
/// <returns>Log sink or <see langword="null"/> if log level is not enabled.</returns>
public static ParametrizedLogger? TryGet(LogEventLevel level)
{
Log(LogEventLevel.Debug, area, source, messageTemplate, propertyValues);
}
if (!IsEnabled(level))
{
return null;
}
/// <summary>
/// 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);
return new ParametrizedLogger(Sink, level);
}
/// <summary>
/// Logs an event with the <see cref="LogEventLevel.Warning"/> level.
/// Returns parametrized logging sink if given log level is enabled.
/// </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 Warning(
string area,
object source,
string messageTemplate,
params object[] propertyValues)
/// <param name="level">The log event level.</param>
/// <param name="outLogger">Log sink that is valid only if method returns <see langword="true"/>.</param>
/// <returns><see langword="true"/> if logger was obtained successfully.</returns>
public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
{
Log(LogEventLevel.Warning, area, source, messageTemplate, propertyValues);
}
ParametrizedLogger? logger = TryGet(level);
/// <summary>
/// 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);
}
outLogger = logger.GetValueOrDefault();
/// <summary>
/// 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);
return logger.HasValue;
}
}
}

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
{
Logger.Error(
Logger.TryGet(LogEventLevel.Error)?.Log(
LogArea.Binding,
Owner,
"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,
File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
ProjectDirectory, OutputPath);
ProjectDirectory, OutputPath, VerifyIl);
if (!res.Success)
return false;
if (!res.WrittenFile)
@ -66,6 +66,8 @@ namespace Avalonia.Build.Tasks
public string ProjectDirectory { get; set; }
public string OutputPath { get; set; }
public bool VerifyIl { get; set; }
public IBuildEngine BuildEngine { 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],
OutputPath = args[2],
BuildEngine = new ConsoleBuildEngine(),
ProjectDirectory = Directory.GetCurrentDirectory()
ProjectDirectory = Directory.GetCurrentDirectory(),
VerifyIl = true
}.Execute() ?
0 :
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,
string output)
string output, bool verifyIl)
{
var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input);
var asm = typeSystem.TargetAssemblyDefinition;
@ -65,7 +65,7 @@ namespace Avalonia.Build.Tasks
var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass) { EnableIlVerification = verifyIl };
var editorBrowsableAttribute = typeSystem
.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");
ItemsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnItemsPropertyChanged);
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnCanUserResizeColumnsChanged);
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnColumnWidthChanged);
RowBackgroundProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowBackgroundChanged);
AlternatingRowBackgroundProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowBackgroundChanged);
FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>(x => x.OnFrozenColumnCountChanged);
GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>(x => x.OnGridLinesVisibilityChanged);
HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>(x => x.OnHeadersVisibilityChanged);
HorizontalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>(x => x.OnHorizontalGridLinesBrushChanged);
IsReadOnlyProperty.Changed.AddClassHandler<DataGrid>(x => x.OnIsReadOnlyChanged);
MaxColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnMaxColumnWidthChanged);
MinColumnWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnMinColumnWidthChanged);
RowHeightProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowHeightChanged);
RowHeaderWidthProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowHeaderWidthChanged);
SelectionModeProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectionModeChanged);
VerticalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>(x => x.OnVerticalGridLinesBrushChanged);
SelectedIndexProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectedIndexChanged);
SelectedItemProperty.Changed.AddClassHandler<DataGrid>(x => x.OnSelectedItemChanged);
IsEnabledProperty.Changed.AddClassHandler<DataGrid>(x => x.DataGrid_IsEnabledChanged);
AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler<DataGrid>(x => x.OnAreRowGroupHeadersFrozenChanged);
RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowDetailsTemplateChanged);
RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>(x => x.OnRowDetailsVisibilityModeChanged);
AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>(x => x.OnAutoGenerateColumnsChanged);
ItemsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsPropertyChanged(e));
CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));
RowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
AlternatingRowBackgroundProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowBackgroundChanged(e));
FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e));
GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnGridLinesVisibilityChanged(e));
HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHeadersVisibilityChanged(e));
HorizontalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHorizontalGridLinesBrushChanged(e));
IsReadOnlyProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnIsReadOnlyChanged(e));
MaxColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMaxColumnWidthChanged(e));
MinColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMinColumnWidthChanged(e));
RowHeightProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeightChanged(e));
RowHeaderWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeaderWidthChanged(e));
SelectionModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectionModeChanged(e));
VerticalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnVerticalGridLinesBrushChanged(e));
SelectedIndexProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedIndexChanged(e));
SelectedItemProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedItemChanged(e));
IsEnabledProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.DataGrid_IsEnabledChanged(e));
AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAreRowGroupHeadersFrozenChanged(e));
RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsTemplateChanged(e));
RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAutoGenerateColumnsChanged(e));
}
/// <summary>
@ -3533,7 +3533,7 @@ namespace Avalonia.Controls
if (AreColumnHeadersVisible &&
_vScrollBar != null && _vScrollBar.IsVisible)
{
_topRightCornerHeader.IsVisible = true; ;
_topRightCornerHeader.IsVisible = true;
}
else
{
@ -5594,7 +5594,7 @@ namespace Avalonia.Controls
{
// This will trigger a call to this method via Cells_SizeChanged for
// which no processing is needed.
_vScrollBar.IsVisible = true; ;
_vScrollBar.IsVisible = true;
if (_vScrollBar.DesiredSize.Width == 0)
{
// 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()
{
PointerPressedEvent.AddClassHandler<DataGridCell>(
x => x.DataGridCell_PointerPressed, handledEventsToo: true);
(x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
}
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()
{
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>(x => x.OnAreSeparatorsVisibleChanged);
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x,e) => x.OnAreSeparatorsVisibleChanged(e));
}
/// <summary>
@ -468,7 +468,7 @@ namespace Avalonia.Controls
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;
}

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

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

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

@ -109,7 +109,7 @@ namespace Avalonia.Controls
static DataGridRowGroupHeader()
{
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>(x => x.OnSublevelIndentChanged);
SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>((x,e) => x.OnSublevelIndentChanged(e));
}
/// <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 Action _optionsInitializers;
private Func<Application> _appFactory;
private IApplicationLifetime _lifetime;
/// <summary>
/// Gets or sets the <see cref="IRuntimePlatform"/> instance.
/// </summary>
@ -30,10 +32,15 @@ namespace Avalonia.Controls
public Action RuntimePlatformServicesInitializer { get; private set; }
/// <summary>
/// Gets or sets the <see cref="Application"/> instance being initialized.
/// Gets the <see cref="Application"/> instance being initialized.
/// </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>
/// Gets or sets a method to call the initialize the windowing subsystem.
/// </summary>
@ -76,20 +83,11 @@ namespace Avalonia.Controls
public static TAppBuilder Configure<TApp>()
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()
{
Instance = app,
ApplicationType = typeof(TApp),
// Needed for CoreRT compatibility
_appFactory = () => new TApp()
};
}
@ -157,6 +155,18 @@ namespace Avalonia.Controls
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>
/// Specifies a windowing subsystem to use.
/// </summary>
@ -254,11 +264,6 @@ namespace Avalonia.Controls
/// </summary>
private void Setup()
{
if (Instance == null)
{
throw new InvalidOperationException("No App instance configured.");
}
if (RuntimePlatformServicesInitializer == null)
{
throw new InvalidOperationException("No runtime platform services configured.");
@ -285,6 +290,9 @@ namespace Avalonia.Controls
WindowingSubsystemInitializer();
RenderingSubsystemInitializer();
AfterPlatformServicesSetupCallback(Self);
Instance = _appFactory();
Instance.ApplicationLifetime = _lifetime;
AvaloniaLocator.CurrentMutable.BindToSelf(Instance);
Instance.RegisterServices();
Instance.Initialize();
AfterSetupCallback(Self);

19
src/Avalonia.Controls/Application.cs

@ -32,7 +32,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
/// </remarks>
public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
public class Application : AvaloniaObject, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
{
/// <summary>
/// The application-global data templates.
@ -210,5 +210,22 @@ namespace Avalonia
{
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()
{
var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode};
builder.Instance.ApplicationLifetime = lifetime;
builder.SetupWithoutStarting();
builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}
}

18
src/Avalonia.Controls/AutoCompleteBox.cs

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

2
src/Avalonia.Controls/Button.cs

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

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

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

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

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

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

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

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

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

12
src/Avalonia.Controls/Carousel.cs

@ -84,17 +84,5 @@ namespace Avalonia.Controls
--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);
FocusableProperty.OverrideDefaultValue<ComboBox>(true);
SelectedItemProperty.Changed.AddClassHandler<ComboBox>(x => x.SelectedItemChanged);
KeyDownEvent.AddClassHandler<ComboBox>(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
SelectedItemProperty.Changed.AddClassHandler<ComboBox>((x,e) => x.SelectedItemChanged(e));
KeyDownEvent.AddClassHandler<ComboBox>((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
}
/// <summary>

2
src/Avalonia.Controls/ContentControl.cs

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

2
src/Avalonia.Controls/ContextMenu.cs

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

2
src/Avalonia.Controls/DataValidationErrors.cs

@ -56,7 +56,7 @@ namespace Avalonia.Controls
{
ErrorsProperty.Changed.Subscribe(ErrorsChanged);
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)

2
src/Avalonia.Controls/Decorator.cs

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

4
src/Avalonia.Controls/DropDown.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls
{
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);
@ -20,7 +20,7 @@ namespace Avalonia.Controls
{
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);

2
src/Avalonia.Controls/Expander.cs

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

11
src/Avalonia.Controls/Grid.cs

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

12
src/Avalonia.Controls/ItemsControl.cs

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

8
src/Avalonia.Controls/LayoutTransformControl.cs

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

2
src/Avalonia.Controls/MenuBase.cs

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

16
src/Avalonia.Controls/MenuItem.cs

@ -102,13 +102,13 @@ namespace Avalonia.Controls
SelectableMixin.Attach<MenuItem>(IsSelectedProperty);
CommandProperty.Changed.Subscribe(CommandChanged);
FocusableProperty.OverrideDefaultValue<MenuItem>(true);
HeaderProperty.Changed.AddClassHandler<MenuItem>(x => x.HeaderChanged);
IconProperty.Changed.AddClassHandler<MenuItem>(x => x.IconChanged);
IsSelectedProperty.Changed.AddClassHandler<MenuItem>(x => x.IsSelectedChanged);
HeaderProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.HeaderChanged(e));
IconProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.IconChanged(e));
IsSelectedProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.IsSelectedChanged(e));
ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel);
ClickEvent.AddClassHandler<MenuItem>(x => x.OnClick);
SubmenuOpenedEvent.AddClassHandler<MenuItem>(x => x.OnSubmenuOpened);
IsSubMenuOpenProperty.Changed.AddClassHandler<MenuItem>(x => x.SubMenuOpenChanged);
ClickEvent.AddClassHandler<MenuItem>((x, e) => x.OnClick(e));
SubmenuOpenedEvent.AddClassHandler<MenuItem>((x, e) => x.OnSubmenuOpened(e));
IsSubMenuOpenProperty.Changed.AddClassHandler<MenuItem>((x, e) => x.SubMenuOpenChanged(e));
}
public MenuItem()
@ -337,7 +337,7 @@ namespace Avalonia.Controls
{
base.OnPointerEnter(e);
var point = e.GetPointerPoint(null);
var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerEnterItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
e.Timestamp, point.Properties, e.KeyModifiers));
}
@ -347,7 +347,7 @@ namespace Avalonia.Controls
{
base.OnPointerLeave(e);
var point = e.GetPointerPoint(null);
var point = e.GetCurrentPoint(null);
RaiseEvent(new PointerEventArgs(PointerLeaveItemEvent, this, e.Pointer, this.VisualRoot, point.Position,
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.Parent.SelectedItem?.IsSubMenuOpen == true)
if (item != item.Parent.SelectedItem &&
item.Parent.SelectedItem?.IsSubMenuOpen == true)
{
item.Parent.SelectedItem.Close();
SelectItemAndAncestors(item);
@ -356,7 +357,7 @@ namespace Avalonia.Controls.Platform
{
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);
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 Avalonia.Controls;
using Avalonia.Input;
namespace Avalonia.Platform
{
@ -57,28 +58,28 @@ namespace Avalonia.Platform
/// Return true to prevent the underlying implementation from closing.
/// </summary>
Func<bool> Closing { get; set; }
/// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary>
void BeginMoveDrag();
void BeginMoveDrag(PointerPressedEventArgs e);
/// <summary>
/// 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
/// </summary>
void BeginResizeDrag(WindowEdge edge);
void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e);
/// <summary>
/// Sets the client size of the top level.
/// </summary>
void Resize(Size clientSize);
/// <summary>
/// Sets the client size of the top level.
/// </summary>
void Move(PixelPoint point);
/// <summary>
/// Minimum width of the window.
/// </summary>

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

@ -21,7 +21,7 @@ namespace Avalonia.Platform
private DragDropEffects _allowedEffects;
private IDataObject _draggedData;
private IInputElement _lastRoot;
private IInputRoot _lastRoot;
private Point _lastPosition;
private StandardCursorType _lastCursorType;
private object _originalCursor;
@ -56,7 +56,7 @@ namespace Avalonia.Platform
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;
@ -91,7 +91,7 @@ namespace Avalonia.Platform
return StandardCursorType.No;
}
private void UpdateCursor(IInputElement root, DragDropEffects effect)
private void UpdateCursor(IInputRoot root, DragDropEffects effect)
{
if (_lastRoot != root)
{

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

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

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

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

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

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

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

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

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

@ -73,7 +73,7 @@ namespace Avalonia.Controls.Presenters
static ScrollContentPresenter()
{
ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>(x => x.ChildChanged);
ChildProperty.Changed.AddClassHandler<ScrollContentPresenter>((x, e) => x.ChildChanged(e));
AffectsArrange<ScrollContentPresenter>(OffsetProperty);
}
@ -246,7 +246,7 @@ namespace Avalonia.Controls.Presenters
if (isLogical)
_activeLogicalGestureScrolls?.TryGetValue(e.Id, out delta);
delta += e.Delta;
if (Extent.Height > Viewport.Height)
{
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);
/// <inheritdoc/>

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

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

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

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

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

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

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

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

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

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

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

@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives
/// <summary>
/// The root window of a <see cref="Popup"/>.
/// </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 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.Horizontal, ":horizontal");
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble);
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble);
}
/// <summary>

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

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

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

@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives
static TemplatedControl()
{
ClipToBoundsProperty.OverrideDefaultValue<TemplatedControl>(true);
TemplateProperty.Changed.AddClassHandler<TemplatedControl>(x => x.OnTemplateChanged);
TemplateProperty.Changed.AddClassHandler<TemplatedControl>((x, e) => x.OnTemplateChanged(e));
}
/// <summary>
@ -255,7 +255,7 @@ namespace Avalonia.Controls.Primitives
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);
ApplyTemplatedParent(child);

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

@ -22,9 +22,9 @@ namespace Avalonia.Controls.Primitives
static Thumb()
{
DragStartedEvent.AddClassHandler<Thumb>(x => x.OnDragStarted, RoutingStrategies.Bubble);
DragDeltaEvent.AddClassHandler<Thumb>(x => x.OnDragDelta, RoutingStrategies.Bubble);
DragCompletedEvent.AddClassHandler<Thumb>(x => x.OnDragCompleted, RoutingStrategies.Bubble);
DragStartedEvent.AddClassHandler<Thumb>((x,e) => x.OnDragStarted(e), RoutingStrategies.Bubble);
DragDeltaEvent.AddClassHandler<Thumb>((x, e) => x.OnDragDelta(e), RoutingStrategies.Bubble);
DragCompletedEvent.AddClassHandler<Thumb>((x, e) => x.OnDragCompleted(e), RoutingStrategies.Bubble);
}
public event EventHandler<VectorEventArgs> DragStarted
@ -83,6 +83,8 @@ namespace Avalonia.Controls.Primitives
Vector = (Vector)_lastPoint,
};
PseudoClasses.Add(":pressed");
RaiseEvent(ev);
}
@ -102,6 +104,8 @@ namespace Avalonia.Controls.Primitives
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.Horizontal, ":horizontal");
ThumbProperty.Changed.AddClassHandler<Track>(x => x.ThumbChanged);
IncreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
DecreaseButtonProperty.Changed.AddClassHandler<Track>(x => x.ButtonChanged);
ThumbProperty.Changed.AddClassHandler<Track>((x,e) => x.ThumbChanged(e));
IncreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
DecreaseButtonProperty.Changed.AddClassHandler<Track>((x, e) => x.ButtonChanged(e));
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 _columns;
static UniformGrid()
{
AffectsMeasure<UniformGrid>(RowsProperty, ColumnsProperty, FirstColumnProperty);
}
/// <summary>
/// Specifies the row count. If set to 0, row count will be calculated automatically.
/// </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.
using System;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
@ -38,8 +39,8 @@ namespace Avalonia.Controls
PseudoClass<ProgressBar, Orientation>(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
PseudoClass<ProgressBar>(IsIndeterminateProperty, ":indeterminate");
ValueProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged);
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>(x => x.UpdateIndicatorWhenPropChanged);
ValueProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
IsIndeterminateProperty.Changed.AddClassHandler<ProgressBar>((x, e) => x.UpdateIndicatorWhenPropChanged(e));
}
public bool IsIndeterminate

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

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

4
src/Avalonia.Controls/RepeatButton.cs

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

4
src/Avalonia.Controls/ScrollViewer.cs

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

6
src/Avalonia.Controls/Slider.cs

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

2
src/Avalonia.Controls/TabControl.cs

@ -196,7 +196,7 @@ namespace Avalonia.Controls
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);
if (container != null

4
src/Avalonia.Controls/TabItem.cs

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

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

Loading…
Cancel
Save