diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings
index 1361172fff..7060f4a62a 100644
--- a/Avalonia.sln.DotSettings
+++ b/Avalonia.sln.DotSettings
@@ -3,6 +3,7 @@
ExplicitlyExcluded
ExplicitlyExcluded
ExplicitlyExcluded
+ DO_NOT_SHOW
HINT
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
diff --git a/build/Base.props b/build/Base.props
index a60373ebb3..100c9088cd 100644
--- a/build/Base.props
+++ b/build/Base.props
@@ -1,5 +1,6 @@
+
diff --git a/build/BuildTargets.targets b/build/BuildTargets.targets
index 08ec039ec7..a5543cd050 100644
--- a/build/BuildTargets.targets
+++ b/build/BuildTargets.targets
@@ -2,6 +2,7 @@
$(MSBuildThisFileDirectory)\..\src\Avalonia.Build.Tasks\bin\$(Configuration)\netstandard2.0\Avalonia.Build.Tasks.dll
true
+ true
diff --git a/global.json b/global.json
index 6d12c28846..1e599211d4 100644
--- a/global.json
+++ b/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"
}
}
diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h
index e54f3fa6a7..f1c7664c3e 100644
--- a/native/Avalonia.Native/inc/avalonia-native.h
+++ b/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();
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
index 1870ef7ab3..c0a49382a7 100644
--- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
+++ b/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 = ""; };
+ 37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = ""; };
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = ""; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; };
@@ -32,6 +35,7 @@
37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; };
37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; };
+ 520624B222973F4100C4DCEF /* menu.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = menu.mm; sourceTree = ""; };
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; };
@@ -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 */,
diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000..18d981003d
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 45ec40c361..10534dea26 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/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 inline T* objc_cast(id from) {
return nil;
}
+@interface ActionCallback : NSObject
+- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
+- (void) action;
+@end
+
#endif
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index 70bd1e67f6..9418782fd1 100644
--- a/native/Avalonia.Native/src/OSX/main.mm
+++ b/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(
+ 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(
+ CFBundleGetFunctionPointerForName(
+ launch_services_bundle,
+ CFSTR("_LSSetApplicationInformationItem")));
+ if (!ls_set_application_information_item_func){}
+ //LOG(ERROR) << "Could not find _LSSetApplicationInformationItem";
+
+ CFStringRef* key_pointer = reinterpret_cast(
+ 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
{
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()
diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h
new file mode 100644
index 0000000000..befbe6a7e0
--- /dev/null
+++ b/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
+{
+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
+{
+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
+
diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm
new file mode 100644
index 0000000000..1d2f075ccb
--- /dev/null
+++ b/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(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(item);
+
+ if(avnMenuItem != nullptr)
+ {
+ [_native addItem: avnMenuItem->GetNative()];
+ }
+
+ return S_OK;
+}
+
+HRESULT AvnAppMenu::RemoveItem (IAvnAppMenuItem* item)
+{
+ auto avnMenuItem = dynamic_cast(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(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;
+}
+
+
diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm
index 297097584a..e7abedae51 100644
--- a/native/Avalonia.Native/src/OSX/platformthreading.mm
+++ b/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 _callback;
diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h
index e2221217f3..932bc56a2e 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/native/Avalonia.Native/src/OSX/window.h
@@ -15,11 +15,13 @@ class WindowBaseImpl;
@end
@interface AvnWindow : NSWindow
++(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
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 3347d58004..2a5acaea91 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -5,6 +5,7 @@
#include "window.h"
#include "KeyTransform.h"
#include "cursor.h"
+#include "menu.h"
#include
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(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 _parent;
bool _canBecomeKeyAndMain;
bool _closed;
+ NSMenu* _menu;
+ bool _isAppMenuApplied;
+}
+
++(void)closeAll
+{
+ NSArray* 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(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];
}
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index a455e2b3fc..552713f94b 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -53,6 +53,7 @@
$(IntermediateOutputPath)/Avalonia/references
$(IntermediateOutputPath)/Avalonia/original.dll
+ false
screenshot). 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 NuGet 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
diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
index bc76a39f08..5b82e2caee 100644
--- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
+++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj
@@ -16,7 +16,7 @@
Resources\Resource.Designer.cs
Off
False
- v8.0
+ v9.0
Properties\AndroidManifest.xml
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index 157609088f..40d001a195 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -20,7 +20,7 @@ namespace ControlCatalog.Android
{
if (Avalonia.Application.Current == null)
{
- AppBuilder.Configure(new App())
+ AppBuilder.Configure()
.UseAndroid()
.SetupWithoutStarting();
Content = new MainView();
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index d683092edf..854cae484c 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -55,7 +55,11 @@ namespace ControlCatalog.NetCore
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
.UsePlatformDetect()
- .With(new X11PlatformOptions { EnableMultiTouch = true })
+ .With(new X11PlatformOptions
+ {
+ EnableMultiTouch = true,
+ UseDBusMenu = true
+ })
.With(new Win32PlatformOptions
{
EnableMultitouch = true,
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index 2f6d25c089..335c460b40 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -2,9 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index 07c42c60c4..4fc63ea054 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/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()
diff --git a/samples/ControlCatalog/DecoratedWindow.xaml b/samples/ControlCatalog/DecoratedWindow.xaml
index cb6016b324..8e4c97b7f0 100644
--- a/samples/ControlCatalog/DecoratedWindow.xaml
+++ b/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">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/DecoratedWindow.xaml.cs b/samples/ControlCatalog/DecoratedWindow.xaml.cs
index 2e7218b956..bdf5b8fbee 100644
--- a/samples/ControlCatalog/DecoratedWindow.xaml.cs
+++ b/samples/ControlCatalog/DecoratedWindow.xaml.cs
@@ -18,18 +18,18 @@ namespace ControlCatalog
{
var ctl = this.FindControl(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("TitleBar").PointerPressed += delegate
+ this.FindControl("TitleBar").PointerPressed += (i, e) =>
{
- PlatformImpl?.BeginMoveDrag();
+ PlatformImpl?.BeginMoveDrag(e);
};
SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West);
SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East);
diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml
index 9527ac3b4e..6088f2ec57 100644
--- a/samples/ControlCatalog/MainWindow.xaml
+++ b/samples/ControlCatalog/MainWindow.xaml
@@ -8,7 +8,36 @@
xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs
index 95c65ed92f..7b0ee897c4 100644
--- a/samples/ControlCatalog/MainWindow.xaml.cs
+++ b/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
diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml
index e1a5cf2c5a..868f0df6ad 100644
--- a/samples/ControlCatalog/Pages/MenuPage.xaml
+++ b/samples/ControlCatalog/Pages/MenuPage.xaml
@@ -3,8 +3,11 @@
x:Class="ControlCatalog.Pages.MenuPage">
Menu
+ Exported menu fallback
+ (Should be only visible on platforms without desktop-global menu bar)
+
A window menu
-
+
(this T builder) where T : AppBuilderBase, 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().ToTransient()
@@ -55,7 +55,7 @@ namespace Avalonia.Android
.Bind().ToConstant(new DefaultRenderTimer(60))
.Bind().ToConstant(new RenderLoop())
.Bind().ToSingleton()
- .Bind().ToConstant(new AssetLoader(app.GetType().Assembly));
+ .Bind().ToConstant(new AssetLoader(appType.Assembly));
SkiaPlatform.Initialize();
((global::Android.App.Application) global::Android.App.Application.Context.ApplicationContext)
diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj
index 0089ea3b8d..c170e8449c 100644
--- a/src/Android/Avalonia.Android/Avalonia.Android.csproj
+++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj
@@ -1,6 +1,6 @@
- monoandroid80
+ monoandroid90
true
diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
index 1b2b205d45..2f95a6e4bd 100644
--- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
+++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
@@ -16,7 +16,7 @@
Resources\Resource.Designer.cs
Off
False
- v8.0
+ v9.0
Properties\AndroidManifest.xml
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 8cc512d132..2450f1a3a1 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/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
/// The binding error.
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
/// The priority.
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}",
diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
index 393482cccf..ad1cefd4ea 100644
--- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs
+++ b/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
/// The property changed observable.
/// Given a TTarget, returns the handler.
/// A disposable that can be used to terminate the subscription.
+ [Obsolete("Use overload taking Action.")]
public static IDisposable AddClassHandler(
this IObservable observable,
Func> handler)
@@ -238,9 +239,7 @@ namespace Avalonia
Func> handler)
where TTarget : class
{
- var target = e.Sender as TTarget;
-
- if (target != null)
+ if (e.Sender is TTarget target)
{
handler(target)(e);
}
diff --git a/src/Avalonia.Base/Data/Core/BindingExpression.cs b/src/Avalonia.Base/Data/Core/BindingExpression.cs
index 7f8396cdfa..986e2cf012 100644
--- a/src/Avalonia.Base/Data/Core/BindingExpression.cs
+++ b/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}",
diff --git a/src/Avalonia.Base/Logging/ILogSink.cs b/src/Avalonia.Base/Logging/ILogSink.cs
index 0ed4eede8f..8b5751b0af 100644
--- a/src/Avalonia.Base/Logging/ILogSink.cs
+++ b/src/Avalonia.Base/Logging/ILogSink.cs
@@ -8,6 +8,77 @@ namespace Avalonia.Logging
///
public interface ILogSink
{
+ ///
+ /// Checks if given log level is enabled.
+ ///
+ /// The log event level.
+ /// if given log level is enabled.
+ bool IsEnabled(LogEventLevel level);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1);
+
+ ///
+ /// Logs an event.
+ ///
+ /// The log event level.
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ void Log(
+ LogEventLevel level,
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2);
+
///
/// Logs a new event.
///
diff --git a/src/Avalonia.Base/Logging/Logger.cs b/src/Avalonia.Base/Logging/Logger.cs
index b1132ff4a9..c895c70094 100644
--- a/src/Avalonia.Base/Logging/Logger.cs
+++ b/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
{
///
@@ -16,124 +14,43 @@ namespace Avalonia.Logging
public static ILogSink Sink { get; set; }
///
- /// Logs an event.
+ /// Checks if given log level is enabled.
///
/// The log event level.
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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);
- }
-
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Verbose(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// if given log level is enabled.
+ public static bool IsEnabled(LogEventLevel level)
{
- Log(LogEventLevel.Verbose, area, source, messageTemplate, propertyValues);
+ return Sink?.IsEnabled(level) == true;
}
///
- /// Logs an event with the level.
+ /// Returns parametrized logging sink if given log level is enabled.
///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Debug(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// The log event level.
+ /// Log sink or if log level is not enabled.
+ public static ParametrizedLogger? TryGet(LogEventLevel level)
{
- Log(LogEventLevel.Debug, area, source, messageTemplate, propertyValues);
- }
+ if (!IsEnabled(level))
+ {
+ return null;
+ }
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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);
}
///
- /// Logs an event with the level.
+ /// Returns parametrized logging sink if given log level is enabled.
///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Warning(
- string area,
- object source,
- string messageTemplate,
- params object[] propertyValues)
+ /// The log event level.
+ /// Log sink that is valid only if method returns .
+ /// if logger was obtained successfully.
+ public static bool TryGet(LogEventLevel level, out ParametrizedLogger outLogger)
{
- Log(LogEventLevel.Warning, area, source, messageTemplate, propertyValues);
- }
+ ParametrizedLogger? logger = TryGet(level);
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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();
- ///
- /// Logs an event with the level.
- ///
- /// The area that the event originates.
- /// The object from which the event originates.
- /// The message template.
- /// The message property values.
- [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;
}
}
}
diff --git a/src/Avalonia.Base/Logging/ParametrizedLogger.cs b/src/Avalonia.Base/Logging/ParametrizedLogger.cs
new file mode 100644
index 0000000000..1550cc1b40
--- /dev/null
+++ b/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
+{
+ ///
+ /// Logger sink parametrized for given logging level.
+ ///
+ public readonly struct ParametrizedLogger
+ {
+ private readonly ILogSink _sink;
+ private readonly LogEventLevel _level;
+
+ public ParametrizedLogger(ILogSink sink, LogEventLevel level)
+ {
+ _sink = sink;
+ _level = level;
+ }
+
+ ///
+ /// Checks if this logger can be used.
+ ///
+ public bool IsValid => _sink != null;
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate)
+ {
+ _sink.Log(_level, area, source, messageTemplate);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ string area,
+ object source,
+ string messageTemplate,
+ T0 propertyValue0,
+ T1 propertyValue1,
+ T2 propertyValue2,
+ T3 propertyValue3)
+ {
+ _sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ 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);
+ }
+
+ ///
+ /// Logs an event.
+ ///
+ /// The area that the event originates.
+ /// The object from which the event originates.
+ /// The message template.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ /// Message property value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Log(
+ 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);
+ }
+ }
+}
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index 2871271062..61184ef7b1 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/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})",
diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
index 61303dbbfe..39ee3f6bca 100644
--- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
+++ b/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; }
diff --git a/src/Avalonia.Build.Tasks/Program.cs b/src/Avalonia.Build.Tasks/Program.cs
index d356b15408..1909c4c6ec 100644
--- a/src/Avalonia.Build.Tasks/Program.cs
+++ b/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;
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index 0dd52c9b48..e348eb0fbc 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/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"))
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index 490a724eda..b65fd2a8b7 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -723,29 +723,29 @@ namespace Avalonia.Controls
PseudoClass(IsValidProperty, x => !x, ":invalid");
- ItemsProperty.Changed.AddClassHandler(x => x.OnItemsPropertyChanged);
- CanUserResizeColumnsProperty.Changed.AddClassHandler(x => x.OnCanUserResizeColumnsChanged);
- ColumnWidthProperty.Changed.AddClassHandler(x => x.OnColumnWidthChanged);
- RowBackgroundProperty.Changed.AddClassHandler(x => x.OnRowBackgroundChanged);
- AlternatingRowBackgroundProperty.Changed.AddClassHandler(x => x.OnRowBackgroundChanged);
- FrozenColumnCountProperty.Changed.AddClassHandler(x => x.OnFrozenColumnCountChanged);
- GridLinesVisibilityProperty.Changed.AddClassHandler(x => x.OnGridLinesVisibilityChanged);
- HeadersVisibilityProperty.Changed.AddClassHandler(x => x.OnHeadersVisibilityChanged);
- HorizontalGridLinesBrushProperty.Changed.AddClassHandler(x => x.OnHorizontalGridLinesBrushChanged);
- IsReadOnlyProperty.Changed.AddClassHandler(x => x.OnIsReadOnlyChanged);
- MaxColumnWidthProperty.Changed.AddClassHandler(x => x.OnMaxColumnWidthChanged);
- MinColumnWidthProperty.Changed.AddClassHandler(x => x.OnMinColumnWidthChanged);
- RowHeightProperty.Changed.AddClassHandler(x => x.OnRowHeightChanged);
- RowHeaderWidthProperty.Changed.AddClassHandler(x => x.OnRowHeaderWidthChanged);
- SelectionModeProperty.Changed.AddClassHandler(x => x.OnSelectionModeChanged);
- VerticalGridLinesBrushProperty.Changed.AddClassHandler(x => x.OnVerticalGridLinesBrushChanged);
- SelectedIndexProperty.Changed.AddClassHandler(x => x.OnSelectedIndexChanged);
- SelectedItemProperty.Changed.AddClassHandler(x => x.OnSelectedItemChanged);
- IsEnabledProperty.Changed.AddClassHandler(x => x.DataGrid_IsEnabledChanged);
- AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler(x => x.OnAreRowGroupHeadersFrozenChanged);
- RowDetailsTemplateProperty.Changed.AddClassHandler(x => x.OnRowDetailsTemplateChanged);
- RowDetailsVisibilityModeProperty.Changed.AddClassHandler(x => x.OnRowDetailsVisibilityModeChanged);
- AutoGenerateColumnsProperty.Changed.AddClassHandler(x => x.OnAutoGenerateColumnsChanged);
+ ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e));
+ CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e));
+ ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e));
+ RowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e));
+ AlternatingRowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e));
+ FrozenColumnCountProperty.Changed.AddClassHandler((x, e) => x.OnFrozenColumnCountChanged(e));
+ GridLinesVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnGridLinesVisibilityChanged(e));
+ HeadersVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnHeadersVisibilityChanged(e));
+ HorizontalGridLinesBrushProperty.Changed.AddClassHandler((x, e) => x.OnHorizontalGridLinesBrushChanged(e));
+ IsReadOnlyProperty.Changed.AddClassHandler((x, e) => x.OnIsReadOnlyChanged(e));
+ MaxColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnMaxColumnWidthChanged(e));
+ MinColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnMinColumnWidthChanged(e));
+ RowHeightProperty.Changed.AddClassHandler((x, e) => x.OnRowHeightChanged(e));
+ RowHeaderWidthProperty.Changed.AddClassHandler((x, e) => x.OnRowHeaderWidthChanged(e));
+ SelectionModeProperty.Changed.AddClassHandler((x, e) => x.OnSelectionModeChanged(e));
+ VerticalGridLinesBrushProperty.Changed.AddClassHandler((x, e) => x.OnVerticalGridLinesBrushChanged(e));
+ SelectedIndexProperty.Changed.AddClassHandler((x, e) => x.OnSelectedIndexChanged(e));
+ SelectedItemProperty.Changed.AddClassHandler((x, e) => x.OnSelectedItemChanged(e));
+ IsEnabledProperty.Changed.AddClassHandler((x, e) => x.DataGrid_IsEnabledChanged(e));
+ AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler((x, e) => x.OnAreRowGroupHeadersFrozenChanged(e));
+ RowDetailsTemplateProperty.Changed.AddClassHandler((x, e) => x.OnRowDetailsTemplateChanged(e));
+ RowDetailsVisibilityModeProperty.Changed.AddClassHandler((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
+ AutoGenerateColumnsProperty.Changed.AddClassHandler((x, e) => x.OnAutoGenerateColumnsChanged(e));
}
///
@@ -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
diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
index a21583b38e..e56c534f50 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs
@@ -29,7 +29,7 @@ namespace Avalonia.Controls
static DataGridCell()
{
PointerPressedEvent.AddClassHandler(
- x => x.DataGridCell_PointerPressed, handledEventsToo: true);
+ (x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
}
public DataGridCell()
{ }
@@ -219,4 +219,4 @@ namespace Avalonia.Controls
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index 6cb0807e29..d1651b2d09 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -67,7 +67,7 @@ namespace Avalonia.Controls
static DataGridColumnHeader()
{
- AreSeparatorsVisibleProperty.Changed.AddClassHandler(x => x.OnAreSeparatorsVisibleChanged);
+ AreSeparatorsVisibleProperty.Changed.AddClassHandler((x,e) => x.OnAreSeparatorsVisibleChanged(e));
}
///
@@ -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;
}
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
index 04a1575486..c9924660be 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs
@@ -116,10 +116,10 @@ namespace Avalonia.Controls
static DataGridRow()
{
- HeaderProperty.Changed.AddClassHandler(x => x.OnHeaderChanged);
- DetailsTemplateProperty.Changed.AddClassHandler(x => x.OnDetailsTemplateChanged);
- AreDetailsVisibleProperty.Changed.AddClassHandler(x => x.OnAreDetailsVisibleChanged);
- PointerPressedEvent.AddClassHandler(x => x.DataGridRow_PointerPressed, handledEventsToo: true);
+ HeaderProperty.Changed.AddClassHandler((x, e) => x.OnHeaderChanged(e));
+ DetailsTemplateProperty.Changed.AddClassHandler((x, e) => x.OnDetailsTemplateChanged(e));
+ AreDetailsVisibleProperty.Changed.AddClassHandler((x, e) => x.OnAreDetailsVisibleChanged(e));
+ PointerPressedEvent.AddClassHandler((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
}
///
diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
index 716997f62c..7dafef9d8b 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
@@ -109,7 +109,7 @@ namespace Avalonia.Controls
static DataGridRowGroupHeader()
{
- SublevelIndentProperty.Changed.AddClassHandler(x => x.OnSublevelIndentChanged);
+ SublevelIndentProperty.Changed.AddClassHandler((x,e) => x.OnSublevelIndentChanged(e));
}
///
@@ -446,4 +446,4 @@ namespace Avalonia.Controls
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs
index 307ddd284c..d9be9171ed 100644
--- a/src/Avalonia.Controls/AppBuilderBase.cs
+++ b/src/Avalonia.Controls/AppBuilderBase.cs
@@ -18,7 +18,9 @@ namespace Avalonia.Controls
{
private static bool s_setupWasAlreadyCalled;
private Action _optionsInitializers;
-
+ private Func _appFactory;
+ private IApplicationLifetime _lifetime;
+
///
/// Gets or sets the instance.
///
@@ -30,10 +32,15 @@ namespace Avalonia.Controls
public Action RuntimePlatformServicesInitializer { get; private set; }
///
- /// Gets or sets the instance being initialized.
+ /// Gets the instance being initialized.
///
- public Application Instance { get; protected set; }
-
+ public Application Instance { get; private set; }
+
+ ///
+ /// Gets the type of the Instance (even if it's not created yet)
+ ///
+ public Type ApplicationType { get; private set; }
+
///
/// Gets or sets a method to call the initialize the windowing subsystem.
///
@@ -76,20 +83,11 @@ namespace Avalonia.Controls
public static TAppBuilder Configure()
where TApp : Application, new()
{
- return Configure(new TApp());
- }
-
- ///
- /// Begin configuring an .
- ///
- /// An instance.
- 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;
}
+ ///
+ /// Sets up the platform-specific services for the application and initialized it with a particular lifetime, but does not run it.
+ ///
+ ///
+ ///
+ public TAppBuilder SetupWithLifetime(IApplicationLifetime lifetime)
+ {
+ _lifetime = lifetime;
+ Setup();
+ return Self;
+ }
+
///
/// Specifies a windowing subsystem to use.
///
@@ -254,11 +264,6 @@ namespace Avalonia.Controls
///
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);
diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index 382106de65..ce60a0f0b9 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -32,7 +32,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
///
- public class Application : IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
+ public class Application : AvaloniaObject, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
{
///
/// The application-global data templates.
@@ -210,5 +210,22 @@ namespace Avalonia
{
ResourcesChanged?.Invoke(this, e);
}
+
+ private string _name;
+ ///
+ /// Defines Name property
+ ///
+ public static readonly DirectProperty NameProperty =
+ AvaloniaProperty.RegisterDirect("Name", o => o.Name, (o, v) => o.Name = v);
+
+ ///
+ /// Application name to be used for various platform-specific purposes
+ ///
+ public string Name
+ {
+ get => _name;
+ set => SetAndRaise(NameProperty, ref _name, value);
+ }
+
}
}
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index abca7a64ee..2533191ae4 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
@@ -125,8 +125,7 @@ namespace Avalonia
where T : AppBuilderBase, new()
{
var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode};
- builder.Instance.ApplicationLifetime = lifetime;
- builder.SetupWithoutStarting();
+ builder.SetupWithLifetime(lifetime);
return lifetime.Start(args);
}
}
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index 1e2fc9f9d0..ce4358648b 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -805,15 +805,15 @@ namespace Avalonia.Controls
{
FocusableProperty.OverrideDefaultValue(true);
- MinimumPopulateDelayProperty.Changed.AddClassHandler(x => x.OnMinimumPopulateDelayChanged);
- IsDropDownOpenProperty.Changed.AddClassHandler(x => x.OnIsDropDownOpenChanged);
- SelectedItemProperty.Changed.AddClassHandler(x => x.OnSelectedItemPropertyChanged);
- TextProperty.Changed.AddClassHandler(x => x.OnTextPropertyChanged);
- SearchTextProperty.Changed.AddClassHandler(x => x.OnSearchTextPropertyChanged);
- FilterModeProperty.Changed.AddClassHandler(x => x.OnFilterModePropertyChanged);
- ItemFilterProperty.Changed.AddClassHandler(x => x.OnItemFilterPropertyChanged);
- ItemsProperty.Changed.AddClassHandler(x => x.OnItemsPropertyChanged);
- IsEnabledProperty.Changed.AddClassHandler(x => x.OnControlIsEnabledChanged);
+ MinimumPopulateDelayProperty.Changed.AddClassHandler((x,e) => x.OnMinimumPopulateDelayChanged(e));
+ IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
+ SelectedItemProperty.Changed.AddClassHandler((x,e) => x.OnSelectedItemPropertyChanged(e));
+ TextProperty.Changed.AddClassHandler((x,e) => x.OnTextPropertyChanged(e));
+ SearchTextProperty.Changed.AddClassHandler((x,e) => x.OnSearchTextPropertyChanged(e));
+ FilterModeProperty.Changed.AddClassHandler((x,e) => x.OnFilterModePropertyChanged(e));
+ ItemFilterProperty.Changed.AddClassHandler((x,e) => x.OnItemFilterPropertyChanged(e));
+ ItemsProperty.Changed.AddClassHandler((x,e) => x.OnItemsPropertyChanged(e));
+ IsEnabledProperty.Changed.AddClassHandler((x,e) => x.OnControlIsEnabledChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index d39ea73828..78d02e200f 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/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;
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 56805e1d6a..beafab3edf 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/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(x => x.OnIsEnabledChanged);
- FirstDayOfWeekProperty.Changed.AddClassHandler(x => x.OnFirstDayOfWeekChanged);
- IsTodayHighlightedProperty.Changed.AddClassHandler(x => x.OnIsTodayHighlightedChanged);
- DisplayModeProperty.Changed.AddClassHandler(x => x.OnDisplayModePropertyChanged);
- SelectionModeProperty.Changed.AddClassHandler(x => x.OnSelectionModeChanged);
- SelectedDateProperty.Changed.AddClassHandler(x => x.OnSelectedDateChanged);
- DisplayDateProperty.Changed.AddClassHandler(x => x.OnDisplayDateChanged);
- DisplayDateStartProperty.Changed.AddClassHandler(x => x.OnDisplayDateStartChanged);
- DisplayDateEndProperty.Changed.AddClassHandler(x => x.OnDisplayDateEndChanged);
- KeyDownEvent.AddClassHandler(x => x.Calendar_KeyDown);
- KeyUpEvent.AddClassHandler(x => x.Calendar_KeyUp);
-
+ IsEnabledProperty.Changed.AddClassHandler((x,e) => x.OnIsEnabledChanged(e));
+ FirstDayOfWeekProperty.Changed.AddClassHandler((x,e) => x.OnFirstDayOfWeekChanged(e));
+ IsTodayHighlightedProperty.Changed.AddClassHandler((x,e) => x.OnIsTodayHighlightedChanged(e));
+ DisplayModeProperty.Changed.AddClassHandler((x,e) => x.OnDisplayModePropertyChanged(e));
+ SelectionModeProperty.Changed.AddClassHandler((x,e) => x.OnSelectionModeChanged(e));
+ SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
+ DisplayDateProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateChanged(e));
+ DisplayDateStartProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateStartChanged(e));
+ DisplayDateEndProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateEndChanged(e));
+ KeyDownEvent.AddClassHandler((x,e) => x.Calendar_KeyDown(e));
+ KeyUpEvent.AddClassHandler((x,e) => x.Calendar_KeyUp(e));
}
///
diff --git a/src/Avalonia.Controls/Calendar/CalendarButton.cs b/src/Avalonia.Controls/Calendar/CalendarButton.cs
index 53852defb3..a273e68d56 100644
--- a/src/Avalonia.Controls/Calendar/CalendarButton.cs
+++ b/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);
}
}
diff --git a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs b/src/Avalonia.Controls/Calendar/CalendarDayButton.cs
index cb2a98e5ca..e62a1ce1f4 100644
--- a/src/Avalonia.Controls/Calendar/CalendarDayButton.cs
+++ b/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);
}
}
diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs
index 55797ae1e3..841b73cd92 100644
--- a/src/Avalonia.Controls/Calendar/DatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/DatePicker.cs
@@ -393,14 +393,14 @@ namespace Avalonia.Controls
{
FocusableProperty.OverrideDefaultValue(true);
- DisplayDateProperty.Changed.AddClassHandler(x => x.OnDisplayDateChanged);
- DisplayDateStartProperty.Changed.AddClassHandler(x => x.OnDisplayDateStartChanged);
- DisplayDateEndProperty.Changed.AddClassHandler(x => x.OnDisplayDateEndChanged);
- IsDropDownOpenProperty.Changed.AddClassHandler(x => x.OnIsDropDownOpenChanged);
- SelectedDateProperty.Changed.AddClassHandler(x => x.OnSelectedDateChanged);
- SelectedDateFormatProperty.Changed.AddClassHandler(x => x.OnSelectedDateFormatChanged);
- CustomDateFormatStringProperty.Changed.AddClassHandler(x => x.OnCustomDateFormatStringChanged);
- TextProperty.Changed.AddClassHandler(x => x.OnTextChanged);
+ DisplayDateProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateChanged(e));
+ DisplayDateStartProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateStartChanged(e));
+ DisplayDateEndProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateEndChanged(e));
+ IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
+ SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
+ SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e));
+ CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e));
+ TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e));
}
///
/// Initializes a new instance of the
diff --git a/src/Avalonia.Controls/Carousel.cs b/src/Avalonia.Controls/Carousel.cs
index 069bf40820..ebc8890721 100644
--- a/src/Avalonia.Controls/Carousel.cs
+++ b/src/Avalonia.Controls/Carousel.cs
@@ -84,17 +84,5 @@ namespace Avalonia.Controls
--SelectedIndex;
}
}
-
- ///
- protected override void OnKeyDown(KeyEventArgs e)
- {
- // Ignore key presses.
- }
-
- ///
- protected override void OnPointerPressed(PointerPressedEventArgs e)
- {
- // Ignore pointer presses.
- }
}
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index a70d26624c..c2cf20b32d 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -65,8 +65,8 @@ namespace Avalonia.Controls
{
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
FocusableProperty.OverrideDefaultValue(true);
- SelectedItemProperty.Changed.AddClassHandler(x => x.SelectedItemChanged);
- KeyDownEvent.AddClassHandler(x => x.OnKeyDown, Interactivity.RoutingStrategies.Tunnel);
+ SelectedItemProperty.Changed.AddClassHandler((x,e) => x.SelectedItemChanged(e));
+ KeyDownEvent.AddClassHandler((x, e) => x.OnKeyDown(e), Interactivity.RoutingStrategies.Tunnel);
}
///
diff --git a/src/Avalonia.Controls/ContentControl.cs b/src/Avalonia.Controls/ContentControl.cs
index 02d7890404..bb3cc4585b 100644
--- a/src/Avalonia.Controls/ContentControl.cs
+++ b/src/Avalonia.Controls/ContentControl.cs
@@ -43,7 +43,7 @@ namespace Avalonia.Controls
static ContentControl()
{
- ContentProperty.Changed.AddClassHandler(x => x.ContentChanged);
+ ContentProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e));
}
///
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index a5025df82d..5dfa5863f5 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/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;
diff --git a/src/Avalonia.Controls/DataValidationErrors.cs b/src/Avalonia.Controls/DataValidationErrors.cs
index 50b387e636..2588b7cc11 100644
--- a/src/Avalonia.Controls/DataValidationErrors.cs
+++ b/src/Avalonia.Controls/DataValidationErrors.cs
@@ -56,7 +56,7 @@ namespace Avalonia.Controls
{
ErrorsProperty.Changed.Subscribe(ErrorsChanged);
HasErrorsProperty.Changed.Subscribe(HasErrorsChanged);
- TemplatedParentProperty.Changed.AddClassHandler(x => x.OnTemplatedParentChange);
+ TemplatedParentProperty.Changed.AddClassHandler((x, e) => x.OnTemplatedParentChange(e));
}
private void OnTemplatedParentChange(AvaloniaPropertyChangedEventArgs e)
diff --git a/src/Avalonia.Controls/Decorator.cs b/src/Avalonia.Controls/Decorator.cs
index 15651b918e..6f16870b9f 100644
--- a/src/Avalonia.Controls/Decorator.cs
+++ b/src/Avalonia.Controls/Decorator.cs
@@ -29,7 +29,7 @@ namespace Avalonia.Controls
static Decorator()
{
AffectsMeasure(ChildProperty, PaddingProperty);
- ChildProperty.Changed.AddClassHandler(x => x.ChildChanged);
+ ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e));
}
///
diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs
index b67d9ef89a..9da803d16d 100644
--- a/src/Avalonia.Controls/DropDown.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs
index 1fa9798784..b2a442b6cc 100644
--- a/src/Avalonia.Controls/Expander.cs
+++ b/src/Avalonia.Controls/Expander.cs
@@ -37,7 +37,7 @@ namespace Avalonia.Controls
PseudoClass(IsExpandedProperty, ":expanded");
- IsExpandedProperty.Changed.AddClassHandler(x => x.OnIsExpandedChanged);
+ IsExpandedProperty.Changed.AddClassHandler((x, e) => x.OnIsExpandedChanged(e));
}
public IPageTransition ContentTransition
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 4f41b0bf1e..8ecfe349f8 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/src/Avalonia.Controls/Grid.cs
@@ -24,13 +24,14 @@ namespace Avalonia.Controls
{
static Grid()
{
- IsSharedSizeScopeProperty.Changed.AddClassHandler(DefinitionBase.OnIsSharedSizeScopePropertyChanged);
ShowGridLinesProperty.Changed.AddClassHandler(OnShowGridLinesPropertyChanged);
- ColumnProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
- ColumnSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
- RowProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
- RowSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
+ IsSharedSizeScopeProperty.Changed.AddClassHandler(DefinitionBase.OnIsSharedSizeScopePropertyChanged);
+ ColumnProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
+ ColumnSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
+ RowProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
+ RowSpanProperty.Changed.AddClassHandler(OnCellAttachedPropertyChanged);
+
AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty);
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 0fe7291835..b027da6d0c 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -64,8 +64,8 @@ namespace Avalonia.Controls
///
static ItemsControl()
{
- ItemsProperty.Changed.AddClassHandler(x => x.ItemsChanged);
- ItemTemplateProperty.Changed.AddClassHandler(x => x.ItemTemplateChanged);
+ ItemsProperty.Changed.AddClassHandler((x, e) => x.ItemsChanged(e));
+ ItemTemplateProperty.Changed.AddClassHandler((x, e) => x.ItemTemplateChanged(e));
}
///
@@ -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;
}
diff --git a/src/Avalonia.Controls/LayoutTransformControl.cs b/src/Avalonia.Controls/LayoutTransformControl.cs
index 1430c39c76..db67a24159 100644
--- a/src/Avalonia.Controls/LayoutTransformControl.cs
+++ b/src/Avalonia.Controls/LayoutTransformControl.cs
@@ -28,11 +28,13 @@ namespace Avalonia.Controls
ClipToBoundsProperty.OverrideDefaultValue(true);
LayoutTransformProperty.Changed
- .AddClassHandler(x => x.OnLayoutTransformChanged);
+ .AddClassHandler((x, e) => x.OnLayoutTransformChanged(e));
ChildProperty.Changed
- .AddClassHandler(x => x.OnChildChanged);
- UseRenderTransformProperty.Changed.AddClassHandler(x => x.OnUseRenderTransformPropertyChanged);
+ .AddClassHandler((x, e) => x.OnChildChanged(e));
+
+ UseRenderTransformProperty.Changed
+ .AddClassHandler((x, e) => x.OnUseRenderTransformPropertyChanged(e));
}
///
diff --git a/src/Avalonia.Controls/MenuBase.cs b/src/Avalonia.Controls/MenuBase.cs
index 8eed58bb4d..be677b5479 100644
--- a/src/Avalonia.Controls/MenuBase.cs
+++ b/src/Avalonia.Controls/MenuBase.cs
@@ -64,7 +64,7 @@ namespace Avalonia.Controls
///
static MenuBase()
{
- MenuItem.SubmenuOpenedEvent.AddClassHandler(x => x.OnSubmenuOpened);
+ MenuItem.SubmenuOpenedEvent.AddClassHandler((x, e) => x.OnSubmenuOpened(e));
}
///
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index 33a708b6a5..3ba0007f6b 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -102,13 +102,13 @@ namespace Avalonia.Controls
SelectableMixin.Attach
Func Closing { get; set; }
-
+
///
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
///
- void BeginMoveDrag();
+ void BeginMoveDrag(PointerPressedEventArgs e);
///
/// 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
///
- void BeginResizeDrag(WindowEdge edge);
-
+ void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e);
+
///
/// Sets the client size of the top level.
///
void Resize(Size clientSize);
-
+
///
/// Sets the client size of the top level.
///
void Move(PixelPoint point);
-
+
///
/// Minimum width of the window.
///
diff --git a/src/Avalonia.Controls/Platform/InProcessDragSource.cs b/src/Avalonia.Controls/Platform/InProcessDragSource.cs
index 6eb056bb32..9d644aaa00 100644
--- a/src/Avalonia.Controls/Platform/InProcessDragSource.cs
+++ b/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)
{
diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
index dedab3e43e..89de24a81a 100644
--- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
@@ -46,8 +46,8 @@ namespace Avalonia.Controls.Presenters
///
static CarouselPresenter()
{
- IsVirtualizedProperty.Changed.AddClassHandler(x => x.IsVirtualizedChanged);
- SelectedIndexProperty.Changed.AddClassHandler(x => x.SelectedIndexChanged);
+ IsVirtualizedProperty.Changed.AddClassHandler((x, e) => x.IsVirtualizedChanged(e));
+ SelectedIndexProperty.Changed.AddClassHandler((x, e) => x.SelectedIndexChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index a5374e7c5a..5e1a844720 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -94,9 +94,9 @@ namespace Avalonia.Controls.Presenters
{
AffectsRender(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsMeasure(BorderThicknessProperty, PaddingProperty);
- ContentProperty.Changed.AddClassHandler(x => x.ContentChanged);
- ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged);
- TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged);
+ ContentProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e));
+ ContentTemplateProperty.Changed.AddClassHandler((x, e) => x.ContentChanged(e));
+ TemplatedParentProperty.Changed.AddClassHandler((x, e) => x.TemplatedParentChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
index 500c7aa187..ab40fbd53b 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Presenters
KeyboardNavigationMode.Once);
VirtualizationModeProperty.Changed
- .AddClassHandler(x => x.VirtualizationModeChanged);
+ .AddClassHandler((x, e) => x.VirtualizationModeChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
index ea56a0c6fc..0f0cdc37cf 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
@@ -45,7 +45,7 @@ namespace Avalonia.Controls.Presenters
///
static ItemsPresenterBase()
{
- TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged);
+ TemplatedParentProperty.Changed.AddClassHandler((x,e) => x.TemplatedParentChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
index ec6a228421..48d0aff551 100644
--- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
@@ -73,7 +73,7 @@ namespace Avalonia.Controls.Presenters
static ScrollContentPresenter()
{
ClipToBoundsProperty.OverrideDefaultValue(typeof(ScrollContentPresenter), true);
- ChildProperty.Changed.AddClassHandler(x => x.ChildChanged);
+ ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e));
AffectsArrange(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);
///
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index debbb81264..5931fec350 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/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);
}
}
}
diff --git a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs b/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
index 3cf50a7b80..d431420a8f 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
@@ -30,7 +30,7 @@ namespace Avalonia.Controls.Primitives
///
static HeaderedContentControl()
{
- ContentProperty.Changed.AddClassHandler(x => x.HeaderChanged);
+ ContentProperty.Changed.AddClassHandler((x, e) => x.HeaderChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
index e0eb0b005f..f4af694f28 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedItemsControl.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives
///
static HeaderedItemsControl()
{
- HeaderProperty.Changed.AddClassHandler(x => x.HeaderChanged);
+ HeaderProperty.Changed.AddClassHandler((x, e) => x.HeaderChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
index 533b643ea6..5e053ed9b4 100644
--- a/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/HeaderedSelectingItemsControl.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives
///
static HeaderedSelectingItemsControl()
{
- HeaderProperty.Changed.AddClassHandler(x => x.HeaderChanged);
+ HeaderProperty.Changed.AddClassHandler((x, e) => x.HeaderChanged(e));
}
///
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index b3f86e8a76..77febf9384 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -93,8 +93,8 @@ namespace Avalonia.Controls.Primitives
static Popup()
{
IsHitTestVisibleProperty.OverrideDefaultValue(false);
- ChildProperty.Changed.AddClassHandler(x => x.ChildChanged);
- IsOpenProperty.Changed.AddClassHandler(x => x.IsOpenChanged);
+ ChildProperty.Changed.AddClassHandler((x, e) => x.ChildChanged(e));
+ IsOpenProperty.Changed.AddClassHandler((x, e) => x.IsOpenChanged(e));
}
public Popup()
diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index b7f0c8f47d..74a6d288f4 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives
///
/// The root window of a .
///
- 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;
diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs
index c6119e89dc..9251ca273f 100644
--- a/src/Avalonia.Controls/Primitives/ScrollBar.cs
+++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs
@@ -58,8 +58,8 @@ namespace Avalonia.Controls.Primitives
PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
- Thumb.DragDeltaEvent.AddClassHandler(o => o.OnThumbDragDelta, RoutingStrategies.Bubble);
- Thumb.DragCompletedEvent.AddClassHandler(o => o.OnThumbDragComplete, RoutingStrategies.Bubble);
+ Thumb.DragDeltaEvent.AddClassHandler((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
+ Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble);
}
///
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index 6869ea0822..7fddee1012 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives
///
static SelectingItemsControl()
{
- IsSelectedChangedEvent.AddClassHandler(x => x.ContainerSelectionChanged);
+ IsSelectedChangedEvent.AddClassHandler((x, e) => x.ContainerSelectionChanged(e));
}
///
@@ -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;
}
}
diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
index 47c3240374..0ace387185 100644
--- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs
+++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs
@@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives
static TemplatedControl()
{
ClipToBoundsProperty.OverrideDefaultValue(true);
- TemplateProperty.Changed.AddClassHandler(x => x.OnTemplateChanged);
+ TemplateProperty.Changed.AddClassHandler((x, e) => x.OnTemplateChanged(e));
}
///
@@ -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);
diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs
index b01ddd5dba..9f5ddb666c 100644
--- a/src/Avalonia.Controls/Primitives/Thumb.cs
+++ b/src/Avalonia.Controls/Primitives/Thumb.cs
@@ -22,9 +22,9 @@ namespace Avalonia.Controls.Primitives
static Thumb()
{
- DragStartedEvent.AddClassHandler(x => x.OnDragStarted, RoutingStrategies.Bubble);
- DragDeltaEvent.AddClassHandler(x => x.OnDragDelta, RoutingStrategies.Bubble);
- DragCompletedEvent.AddClassHandler(x => x.OnDragCompleted, RoutingStrategies.Bubble);
+ DragStartedEvent.AddClassHandler((x,e) => x.OnDragStarted(e), RoutingStrategies.Bubble);
+ DragDeltaEvent.AddClassHandler((x, e) => x.OnDragDelta(e), RoutingStrategies.Bubble);
+ DragCompletedEvent.AddClassHandler((x, e) => x.OnDragCompleted(e), RoutingStrategies.Bubble);
}
public event EventHandler 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");
}
}
}
diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs
index a569808b35..292c65aa06 100644
--- a/src/Avalonia.Controls/Primitives/Track.cs
+++ b/src/Avalonia.Controls/Primitives/Track.cs
@@ -48,9 +48,9 @@ namespace Avalonia.Controls.Primitives
{
PseudoClass