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/Microsoft.Reactive.Testing.props b/build/Microsoft.Reactive.Testing.props
index 777bc4bb53..a0ba2163bb 100644
--- a/build/Microsoft.Reactive.Testing.props
+++ b/build/Microsoft.Reactive.Testing.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props
index 1208be34b8..f827cb9a32 100644
--- a/build/ReactiveUI.props
+++ b/build/ReactiveUI.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/Rx.props b/build/Rx.props
index 359ce53a92..edff0af160 100644
--- a/build/Rx.props
+++ b/build/Rx.props
@@ -1,5 +1,5 @@
-
+
diff --git a/build/System.Memory.props b/build/System.Memory.props
index b328f7fd97..b36998a780 100644
--- a/build/System.Memory.props
+++ b/build/System.Memory.props
@@ -1,5 +1,5 @@
-
+
diff --git a/dirs.proj b/dirs.proj
index e56320e73f..4b3b1183f0 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -8,7 +8,8 @@
-
+
+
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 36e16c24d1..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
{
@@ -52,6 +54,7 @@ struct AvnScreen
{
AvnRect Bounds;
AvnRect WorkingArea;
+ float PixelDensity;
bool Primary;
};
@@ -172,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
@@ -187,7 +195,6 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT Close() = 0;
virtual HRESULT Activate () = 0;
virtual HRESULT GetClientSize(AvnSize*ret) = 0;
- virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0;
virtual HRESULT GetScaling(double*ret)=0;
virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) = 0;
virtual HRESULT Resize(double width, double height) = 0;
@@ -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/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index 9d436b98c5..e7f009787a 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -38,6 +38,8 @@ class Screens : public ComSingleObject
ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width;
+ ret->PixelDensity = [screen backingScaleFactor];
+
ret->Primary = index == 0;
return S_OK;
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..d9dfe36444
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/menu.mm
@@ -0,0 +1,305 @@
+
+#include "common.h"
+#include "menu.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.action = @selector(terminate:);
+ [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..557e19e7a8 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/native/Avalonia.Native/src/OSX/window.h
@@ -20,6 +20,7 @@ class WindowBaseImpl;
-(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 bfa33eb259..dbb437243a 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
@@ -54,7 +55,6 @@ public:
FORWARD_IUNKNOWN()
virtual ~WindowBaseImpl()
{
- NSDebugLog(@"~WindowBaseImpl()");
View = NULL;
Window = NULL;
}
@@ -64,9 +64,11 @@ public:
SoftwareDrawingOperation CurrentSwDrawingOperation;
AvnPoint lastPositionSet;
NSString* _lastTitle;
+ IAvnAppMenu* _mainMenu;
WindowBaseImpl(IAvnWindowBaseEvents* events)
{
+ _mainMenu = nullptr;
BaseEvents = events;
View = [[AvnView alloc] initWithParent:this];
@@ -94,6 +96,7 @@ public:
UpdateStyle();
[Window makeKeyAndOrderFront:Window];
+ [NSApp activateIgnoringOtherApps:YES];
[Window setTitle:_lastTitle];
[Window setTitleVisibility:NSWindowTitleVisible];
@@ -123,6 +126,7 @@ public:
if(Window != nullptr)
{
[Window makeKeyWindow];
+ [NSApp activateIgnoringOtherApps:YES];
}
}
@@ -161,22 +165,6 @@ public:
}
}
- virtual HRESULT GetMaxClientSize(AvnSize* ret) override
- {
- @autoreleasepool
- {
- if(ret == nullptr)
- return E_POINTER;
-
- auto size = [NSScreen.screens objectAtIndex:0].frame.size;
-
- ret->Height = size.height;
- ret->Width = size.width;
-
- return S_OK;
- }
- }
-
virtual HRESULT GetScaling (double* ret) override
{
@autoreleasepool
@@ -226,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
@@ -369,12 +382,9 @@ public:
virtual void UpdateCursor()
{
- [View resetCursorRects];
if (cursor != nil)
{
- auto rect = [Window frame];
- [View addCursorRect:rect cursor:cursor];
- [cursor set];
+ [cursor set];
}
}
@@ -416,8 +426,8 @@ private:
INHERIT_INTERFACE_MAP(WindowBaseImpl)
INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow)
END_INTERFACE_MAP()
- virtual ~WindowImpl(){
- NSDebugLog(@"~WindowImpl");
+ virtual ~WindowImpl()
+ {
}
ComPtr WindowEvents;
@@ -425,6 +435,7 @@ private:
{
WindowEvents = events;
[Window setCanBecomeKeyAndMain];
+ [Window disableCursorRects];
}
virtual HRESULT Show () override
@@ -664,10 +675,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)dealloc
{
- NSDebugLog(@"AvnView dealloc");
}
-
- (void)onClosed
{
_parent = NULL;
@@ -1063,11 +1072,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
ComPtr _parent;
bool _canBecomeKeyAndMain;
bool _closed;
+ NSMenu* _menu;
+ bool _isAppMenuApplied;
}
- (void)dealloc
{
- NSDebugLog(@"AvnWindow dealloc");
}
- (void)pollModalSession:(nonnull NSModalSession)session
@@ -1087,6 +1097,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;
@@ -1179,6 +1215,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];
}
@@ -1223,6 +1277,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/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml
index aa598cf82d..735e2d7102 100644
--- a/samples/BindingDemo/MainWindow.xaml
+++ b/samples/BindingDemo/MainWindow.xaml
@@ -25,6 +25,9 @@
+
diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
index 881799a4c4..300c696c34 100644
--- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
@@ -1,10 +1,11 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
-using ReactiveUI;
+using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Threading;
+using ReactiveUI;
namespace BindingDemo.ViewModels
{
@@ -56,7 +57,7 @@ namespace BindingDemo.ViewModels
public ObservableCollection Items { get; }
public ObservableCollection SelectedItems { get; }
- public ReactiveCommand ShuffleItems { get; }
+ public ReactiveCommand ShuffleItems { get; }
public string BooleanString
{
@@ -89,7 +90,7 @@ namespace BindingDemo.ViewModels
}
public IObservable CurrentTimeObservable { get; }
- public ReactiveCommand StringValueCommand { get; }
+ public ReactiveCommand
-
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
index 1a607342f3..decd849adc 100644
--- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
@@ -74,7 +74,7 @@ namespace ControlCatalog.Pages
private void RepeaterClick(object sender, PointerPressedEventArgs e)
{
- var item = (e.Source as TextBlock)?.DataContext as string;
+ var item = (e.Source as TextBlock)?.DataContext as ItemsRepeaterPageViewModel.Item;
((ItemsRepeaterPageViewModel)DataContext).SelectedItem = item;
}
}
diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
index cdbf8fd2b6..0f2d4461bf 100644
--- a/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
@@ -78,7 +78,7 @@ namespace ControlCatalog.Pages
}
}
- private string GenerateItem() => $"Item {_counter++}";
+ private string GenerateItem() => $"Item {_counter++.ToString()}";
}
}
}
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
-
+
(
- Enumerable.Range(1, 100000).Select(i => $"Item {i}"));
+ Items = new ObservableCollection- (
+ Enumerable.Range(1, 100000).Select(i => new Item
+ {
+ Text = $"Item {i.ToString()}",
+ }));
}
- public ObservableCollection Items { get; }
+ public ObservableCollection
- Items { get; }
- public string SelectedItem { get; set; }
+ public Item SelectedItem { get; set; }
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
- Items.Insert(index + 1, $"New Item {newItemIndex++}");
+ Items.Insert(index + 1, new Item { Text = $"New Item {newItemIndex++}" });
+ }
+
+ public void RandomizeHeights()
+ {
+ var random = new Random();
+
+ foreach (var i in Items)
+ {
+ i.Height = random.Next(240) + 10;
+ }
+ }
+
+ public class Item : ReactiveObject
+ {
+ private double _height = double.NaN;
+
+ public string Text { get; set; }
+
+ public double Height
+ {
+ get => _height;
+ set => this.RaiseAndSetIfChanged(ref _height, value);
+ }
}
}
}
diff --git a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs
index 0cb5e1b87b..d2d789a687 100644
--- a/samples/RenderDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/RenderDemo/ViewModels/MainWindowViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reactive;
using ReactiveUI;
namespace RenderDemo.ViewModels
@@ -26,7 +27,7 @@ namespace RenderDemo.ViewModels
set { this.RaiseAndSetIfChanged(ref drawFps, value); }
}
- public ReactiveCommand ToggleDrawDirtyRects { get; }
- public ReactiveCommand ToggleDrawFps { get; }
+ public ReactiveCommand ToggleDrawDirtyRects { get; }
+ public ReactiveCommand ToggleDrawFps { get; }
}
}
diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
index 93fe09a156..649c64ab1d 100644
--- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
@@ -4,10 +4,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reactive;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
-using ReactiveUI.Legacy;
using ReactiveUI;
using Avalonia.Layout;
@@ -18,7 +18,7 @@ namespace VirtualizationDemo.ViewModels
private int _itemCount = 200;
private string _newItemString = "New Item";
private int _newItemIndex;
- private IReactiveList _items;
+ private AvaloniaList _items;
private string _prefix = "Item";
private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
@@ -54,7 +54,7 @@ namespace VirtualizationDemo.ViewModels
public AvaloniaList SelectedItems { get; }
= new AvaloniaList();
- public IReactiveList Items
+ public AvaloniaList Items
{
get { return _items; }
private set { this.RaiseAndSetIfChanged(ref _items, value); }
@@ -93,11 +93,11 @@ namespace VirtualizationDemo.ViewModels
public IEnumerable VirtualizationModes =>
Enum.GetValues(typeof(ItemVirtualizationMode)).Cast();
- public ReactiveCommand AddItemCommand { get; private set; }
- public ReactiveCommand RecreateCommand { get; private set; }
- public ReactiveCommand RemoveItemCommand { get; private set; }
- public ReactiveCommand SelectFirstCommand { get; private set; }
- public ReactiveCommand SelectLastCommand { get; private set; }
+ public ReactiveCommand AddItemCommand { get; private set; }
+ public ReactiveCommand RecreateCommand { get; private set; }
+ public ReactiveCommand RemoveItemCommand { get; private set; }
+ public ReactiveCommand SelectFirstCommand { get; private set; }
+ public ReactiveCommand SelectLastCommand { get; private set; }
public void RandomizeSize()
{
@@ -123,7 +123,7 @@ namespace VirtualizationDemo.ViewModels
{
var items = Enumerable.Range(0, count)
.Select(x => new ItemViewModel(x));
- Items = new ReactiveList(items);
+ Items = new AvaloniaList(items);
}
else if (count > Items.Count)
{
@@ -162,7 +162,7 @@ namespace VirtualizationDemo.ViewModels
_prefix = _prefix == "Item" ? "Recreated" : "Item";
var items = Enumerable.Range(0, _itemCount)
.Select(x => new ItemViewModel(x, _prefix));
- Items = new ReactiveList(items);
+ Items = new AvaloniaList(items);
}
private void SelectItem(int index)
diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index c91b58311b..c11cadfbac 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -17,7 +17,7 @@ namespace Avalonia
{
public static T UseAndroid(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.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 0d0d9db252..71706676d6 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -15,10 +15,10 @@ using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform
{
class TopLevelImpl : IAndroidView, ITopLevelImpl, IFramebufferPlatformSurface
-
{
private readonly AndroidKeyboardEventsHelper _keyboardHelper;
private readonly AndroidTouchEventsHelper _touchHelper;
+
private ViewImpl _view;
public TopLevelImpl(Context context, bool placeOnTop = false)
@@ -28,6 +28,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_touchHelper = new AndroidTouchEventsHelper(this, () => InputRoot,
p => GetAvaloniaPointFromEvent(p));
+ Surfaces = new object[] { this };
+
MaxClientSize = new Size(_view.Resources.DisplayMetrics.WidthPixels,
_view.Resources.DisplayMetrics.HeightPixels);
}
@@ -82,7 +84,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public IPlatformHandle Handle => _view;
- public IEnumerable