diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b70e0bf77f..54645e461e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -68,7 +68,7 @@ jobs: inputs: script: | brew update - brew install castxml + brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/8a004a91a7fcd3f6620d5b01b6541ff0a640ffba/Formula/castxml.rb - task: CmdLine@2 displayName: 'Install Nuke' diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index 08a9aa3ceb..4f69f39e02 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,6 +1,6 @@  - - + + diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 3475eff654..b10db08adc 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -1,5 +1,6 @@ #include "com.h" #include "key.h" +#include "stddef.h" #define AVNCOM(name, id) COMINTERFACE(name, 2e2cda0a, 9ae5, 4f1b, 8e, 20, 08, 1a, 04, 27, 9f, id) @@ -19,8 +20,9 @@ struct IAvnGlContext; struct IAvnGlDisplay; struct IAvnGlSurfaceRenderTarget; struct IAvnGlSurfaceRenderingSession; -struct IAvnAppMenu; -struct IAvnAppMenuItem; +struct IAvnMenu; +struct IAvnMenuItem; +struct IAvnMenuEvents; enum SystemDecorations { SystemDecorationsNone = 0, @@ -175,6 +177,13 @@ enum AvnWindowEdge WindowEdgeSouthEast }; +enum AvnMenuItemToggleType +{ + None, + CheckMark, + Radio +}; + AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown { public: @@ -188,11 +197,10 @@ public: virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0; virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0; virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** 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; + virtual HRESULT SetAppMenu(IAvnMenu* menu) = 0; + virtual HRESULT CreateMenu (IAvnMenuEvents* cb, IAvnMenu** ppv) = 0; + virtual HRESULT CreateMenuItem (IAvnMenuItem** ppv) = 0; + virtual HRESULT CreateMenuItemSeperator (IAvnMenuItem** ppv) = 0; }; AVNCOM(IAvnString, 17) : IUnknown @@ -222,8 +230,7 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown virtual HRESULT SetTopMost (bool value) = 0; virtual HRESULT SetCursor(IAvnCursor* cursor) = 0; virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0; - virtual HRESULT SetMainMenu(IAvnAppMenu* menu) = 0; - virtual HRESULT ObtainMainMenu(IAvnAppMenu** retOut) = 0; + virtual HRESULT SetMainMenu(IAvnMenu* menu) = 0; virtual HRESULT ObtainNSWindowHandle(void** retOut) = 0; virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0; virtual HRESULT ObtainNSViewHandle(void** retOut) = 0; @@ -388,10 +395,10 @@ AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown virtual HRESULT GetScaling(double* ret) = 0; }; -AVNCOM(IAvnAppMenu, 17) : IUnknown +AVNCOM(IAvnMenu, 17) : IUnknown { - virtual HRESULT AddItem (IAvnAppMenuItem* item) = 0; - virtual HRESULT RemoveItem (IAvnAppMenuItem* item) = 0; + virtual HRESULT InsertItem (int index, IAvnMenuItem* item) = 0; + virtual HRESULT RemoveItem (IAvnMenuItem* item) = 0; virtual HRESULT SetTitle (void* utf8String) = 0; virtual HRESULT Clear () = 0; }; @@ -401,12 +408,23 @@ AVNCOM(IAvnPredicateCallback, 18) : IUnknown virtual bool Evaluate() = 0; }; -AVNCOM(IAvnAppMenuItem, 19) : IUnknown +AVNCOM(IAvnMenuItem, 19) : IUnknown { - virtual HRESULT SetSubMenu (IAvnAppMenu* menu) = 0; + virtual HRESULT SetSubMenu (IAvnMenu* menu) = 0; virtual HRESULT SetTitle (void* utf8String) = 0; virtual HRESULT SetGesture (void* utf8String, AvnInputModifiers modifiers) = 0; virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) = 0; + virtual HRESULT SetIsChecked (bool isChecked) = 0; + virtual HRESULT SetToggleType (AvnMenuItemToggleType toggleType) = 0; + virtual HRESULT SetIcon (void* data, size_t length) = 0; +}; + +AVNCOM(IAvnMenuEvents, 1A) : IUnknown +{ + /** + * NeedsUpdate + */ + virtual void NeedsUpdate () = 0; }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative(); diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme index 1a665d3ea5..5d20a135b9 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme @@ -29,8 +29,6 @@ shouldUseLaunchSchemeArgsEnv = "YES"> - - - - @end -extern NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; +NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular; + @implementation AvnAppDelegate - (void)applicationWillFinishLaunching:(NSNotification *)notification { @@ -14,6 +15,10 @@ extern NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationA } [[NSApplication sharedApplication] setActivationPolicy: AvnDesiredActivationPolicy]; + + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"]; + + [[NSApplication sharedApplication] setHelpMenu: [[NSMenu new] initWithTitle:@""]]; } } diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 85403abfe7..7a433bfd9f 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -15,11 +15,11 @@ extern IAvnScreens* CreateScreens(); extern IAvnClipboard* CreateClipboard(); extern IAvnCursorFactory* CreateCursorFactory(); extern IAvnGlDisplay* GetGlDisplay(); -extern IAvnAppMenu* CreateAppMenu(); -extern IAvnAppMenuItem* CreateAppMenuItem(); -extern IAvnAppMenuItem* CreateAppMenuItemSeperator(); -extern void SetAppMenu (NSString* appName, IAvnAppMenu* appMenu); -extern IAvnAppMenu* GetAppMenu (); +extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* events); +extern IAvnMenuItem* CreateAppMenuItem(); +extern IAvnMenuItem* CreateAppMenuItemSeperator(); +extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); +extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); extern void InitializeAvnApp(); diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index a2134de6c1..a63353bc0a 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -92,12 +92,11 @@ void SetProcessName(NSString* appTitle) { 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 @@ -228,41 +227,29 @@ public: return S_OK; } - virtual HRESULT CreateMenu (IAvnAppMenu** ppv) override + virtual HRESULT CreateMenu (IAvnMenuEvents* cb, IAvnMenu** ppv) override { - *ppv = ::CreateAppMenu(); + *ppv = ::CreateAppMenu(cb); return S_OK; } - virtual HRESULT CreateMenuItem (IAvnAppMenuItem** ppv) override + virtual HRESULT CreateMenuItem (IAvnMenuItem** ppv) override { *ppv = ::CreateAppMenuItem(); return S_OK; } - virtual HRESULT CreateMenuItemSeperator (IAvnAppMenuItem** ppv) override + virtual HRESULT CreateMenuItemSeperator (IAvnMenuItem** ppv) override { *ppv = ::CreateAppMenuItemSeperator(); return S_OK; } - virtual HRESULT SetAppMenu (IAvnAppMenu* appMenu) override + virtual HRESULT SetAppMenu (IAvnMenu* 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 index befbe6a7e0..bfbc6801f8 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -14,8 +14,10 @@ class AvnAppMenuItem; class AvnAppMenu; -@interface AvnMenu : NSMenu // for some reason it doesnt detect nsmenu here but compiler doesnt complain -- (void)setMenu:(NSMenu*) menu; +@interface AvnMenu : NSMenu +- (id) initWithDelegate: (NSObject*) del; +- (void) setHasGlobalMenuItem: (bool) value; +- (bool) hasGlobalMenuItem; @end @interface AvnMenuItem : NSMenuItem @@ -23,13 +25,14 @@ class AvnAppMenu; - (void)didSelectItem:(id)sender; @end -class AvnAppMenuItem : public ComSingleObject +class AvnAppMenuItem : public ComSingleObject { private: NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem IAvnActionCallback* _callback; IAvnPredicateCallback* _predicate; bool _isSeperator; + bool _isCheckable; public: FORWARD_IUNKNOWN() @@ -38,7 +41,7 @@ public: NSMenuItem* GetNative(); - virtual HRESULT SetSubMenu (IAvnAppMenu* menu) override; + virtual HRESULT SetSubMenu (IAvnMenu* menu) override; virtual HRESULT SetTitle (void* utf8String) override; @@ -46,29 +49,36 @@ public: virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override; + virtual HRESULT SetIsChecked (bool isChecked) override; + + virtual HRESULT SetToggleType (AvnMenuItemToggleType toggleType) override; + + virtual HRESULT SetIcon (void* data, size_t length) override; + bool EvaluateItemEnabled(); void RaiseOnClicked(); }; -class AvnAppMenu : public ComSingleObject +class AvnAppMenu : public ComSingleObject { private: AvnMenu* _native; + ComPtr _baseEvents; public: FORWARD_IUNKNOWN() - AvnAppMenu(); - - AvnAppMenu(AvnMenu* native); - + AvnAppMenu(IAvnMenuEvents* events); + AvnMenu* GetNative(); - virtual HRESULT AddItem (IAvnAppMenuItem* item) override; + void RaiseNeedsUpdate (); + + virtual HRESULT InsertItem (int index, IAvnMenuItem* item) override; - virtual HRESULT RemoveItem (IAvnAppMenuItem* item) override; + virtual HRESULT RemoveItem (IAvnMenuItem* item) override; virtual HRESULT SetTitle (void* utf8String) override; @@ -76,5 +86,9 @@ public: }; +@interface AvnMenuDelegate : NSObject +- (id) initWithParent: (AvnAppMenu*) parent; +@end + #endif diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 1d2f075ccb..dc1245cd23 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -4,6 +4,30 @@ #include "window.h" @implementation AvnMenu +{ + bool _isReparented; + NSObject* _wtf; +} + +- (id) initWithDelegate: (NSObject*)del +{ + self = [super init]; + self.delegate = del; + _wtf = del; + _isReparented = false; + return self; +} + +- (bool)hasGlobalMenuItem +{ + return _isReparented; +} + +- (void)setHasGlobalMenuItem:(bool)value +{ + _isReparented = value; +} + @end @implementation AvnMenuItem @@ -46,6 +70,7 @@ AvnAppMenuItem::AvnAppMenuItem(bool isSeperator) { + _isCheckable = false; _isSeperator = isSeperator; if(isSeperator) @@ -65,49 +90,134 @@ NSMenuItem* AvnAppMenuItem::GetNative() return _native; } -HRESULT AvnAppMenuItem::SetSubMenu (IAvnAppMenu* menu) +HRESULT AvnAppMenuItem::SetSubMenu (IAvnMenu* menu) { - auto nsMenu = dynamic_cast(menu)->GetNative(); - - [_native setSubmenu: nsMenu]; - - return S_OK; + @autoreleasepool + { + if(menu != nullptr) + { + auto nsMenu = dynamic_cast(menu)->GetNative(); + + [_native setSubmenu: nsMenu]; + } + else + { + [_native setSubmenu: nullptr]; + } + + return S_OK; + } } HRESULT AvnAppMenuItem::SetTitle (void* utf8String) { - if (utf8String != nullptr) + @autoreleasepool { - [_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; + if (utf8String != nullptr) + { + [_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; + } + + return S_OK; } - - 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; + @autoreleasepool + { + 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; + @autoreleasepool + { + _predicate = predicate; + _callback = callback; + return S_OK; + } +} + +HRESULT AvnAppMenuItem::SetIsChecked (bool isChecked) +{ + @autoreleasepool + { + [_native setState:(isChecked && _isCheckable ? NSOnState : NSOffState)]; + return S_OK; + } +} + +HRESULT AvnAppMenuItem::SetToggleType(AvnMenuItemToggleType toggleType) +{ + @autoreleasepool + { + switch(toggleType) + { + case AvnMenuItemToggleType::None: + [_native setOnStateImage: [NSImage imageNamed:@"NSMenuCheckmark"]]; + + _isCheckable = false; + break; + + case AvnMenuItemToggleType::CheckMark: + [_native setOnStateImage: [NSImage imageNamed:@"NSMenuCheckmark"]]; + + _isCheckable = true; + break; + + case AvnMenuItemToggleType::Radio: + [_native setOnStateImage: [NSImage imageNamed:@"NSMenuItemBullet"]]; + + _isCheckable = true; + break; + } + + return S_OK; + } +} + +HRESULT AvnAppMenuItem::SetIcon(void *data, size_t length) +{ + @autoreleasepool + { + if(data != nullptr) + { + NSData *imageData = [NSData dataWithBytes:data length:length]; + NSImage *image = [[NSImage alloc] initWithData:imageData]; + + NSSize originalSize = [image size]; + + NSSize size; + size.height = [[NSFont menuFontOfSize:0] pointSize] * 1.333333; + + auto scaleFactor = size.height / originalSize.height; + size.width = originalSize.width * scaleFactor; + + [image setSize: size]; + [_native setImage:image]; + } + else + { + [_native setImage:nullptr]; + } + return S_OK; + } } bool AvnAppMenuItem::EvaluateItemEnabled() @@ -130,71 +240,123 @@ void AvnAppMenuItem::RaiseOnClicked() } } -AvnAppMenu::AvnAppMenu() +AvnAppMenu::AvnAppMenu(IAvnMenuEvents* events) { - _native = [AvnMenu new]; + _baseEvents = events; + id del = [[AvnMenuDelegate alloc] initWithParent: this]; + _native = [[AvnMenu alloc] initWithDelegate: del]; } -AvnAppMenu::AvnAppMenu(AvnMenu* native) -{ - _native = native; -} AvnMenu* AvnAppMenu::GetNative() { return _native; } -HRESULT AvnAppMenu::AddItem (IAvnAppMenuItem* item) +void AvnAppMenu::RaiseNeedsUpdate() { - auto avnMenuItem = dynamic_cast(item); - - if(avnMenuItem != nullptr) + if(_baseEvents != nullptr) { - [_native addItem: avnMenuItem->GetNative()]; + _baseEvents->NeedsUpdate(); } - - return S_OK; } -HRESULT AvnAppMenu::RemoveItem (IAvnAppMenuItem* item) +HRESULT AvnAppMenu::InsertItem(int index, IAvnMenuItem *item) { - auto avnMenuItem = dynamic_cast(item); - - if(avnMenuItem != nullptr) + @autoreleasepool { - [_native removeItem:avnMenuItem->GetNative()]; + if([_native hasGlobalMenuItem]) + { + index++; + } + + auto avnMenuItem = dynamic_cast(item); + + if(avnMenuItem != nullptr) + { + [_native insertItem: avnMenuItem->GetNative() atIndex:index]; + } + + return S_OK; + } +} + +HRESULT AvnAppMenu::RemoveItem (IAvnMenuItem* item) +{ + @autoreleasepool + { + auto avnMenuItem = dynamic_cast(item); + + if(avnMenuItem != nullptr) + { + [_native removeItem:avnMenuItem->GetNative()]; + } + + return S_OK; } - - return S_OK; } HRESULT AvnAppMenu::SetTitle (void* utf8String) { - if (utf8String != nullptr) + @autoreleasepool { - [_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; + if (utf8String != nullptr) + { + [_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; + } + + return S_OK; } - - return S_OK; } HRESULT AvnAppMenu::Clear() { - [_native removeAllItems]; - return S_OK; + @autoreleasepool + { + [_native removeAllItems]; + return S_OK; + } +} + +@implementation AvnMenuDelegate +{ + ComPtr _parent; } +- (id) initWithParent:(AvnAppMenu *)parent +{ + self = [super init]; + _parent = parent; + return self; +} +- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel +{ + if(shouldCancel) + return NO; + return YES; +} + +- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu +{ + return [menu numberOfItems]; +} + +- (void)menuNeedsUpdate:(NSMenu *)menu +{ + _parent->RaiseNeedsUpdate(); +} + + +@end -extern IAvnAppMenu* CreateAppMenu() +extern IAvnMenu* CreateAppMenu(IAvnMenuEvents* cb) { @autoreleasepool { - id menuBar = [NSMenu new]; - return new AvnAppMenu(menuBar); + return new AvnAppMenu(cb); } } -extern IAvnAppMenuItem* CreateAppMenuItem() +extern IAvnMenuItem* CreateAppMenuItem() { @autoreleasepool { @@ -202,7 +364,7 @@ extern IAvnAppMenuItem* CreateAppMenuItem() } } -extern IAvnAppMenuItem* CreateAppMenuItemSeperator() +extern IAvnMenuItem* CreateAppMenuItemSeperator() { @autoreleasepool { @@ -210,10 +372,10 @@ extern IAvnAppMenuItem* CreateAppMenuItemSeperator() } } -static IAvnAppMenu* s_appMenu = nullptr; +static IAvnMenu* s_appMenu = nullptr; static NSMenuItem* s_appMenuItem = nullptr; -extern void SetAppMenu (NSString* appName, IAvnAppMenu* menu) +extern void SetAppMenu (NSString* appName, IAvnMenu* menu) { s_appMenu = menu; @@ -294,7 +456,7 @@ extern void SetAppMenu (NSString* appName, IAvnAppMenu* menu) } } -extern IAvnAppMenu* GetAppMenu () +extern IAvnMenu* GetAppMenu () { return s_appMenu; } diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm index 2d72226faf..f93436d157 100644 --- a/native/Avalonia.Native/src/OSX/platformthreading.mm +++ b/native/Avalonia.Native/src/OSX/platformthreading.mm @@ -54,9 +54,11 @@ private: { public: FORWARD_IUNKNOWN() + bool Running = false; bool Cancelled = false; - virtual void Cancel() + + virtual void Cancel() override { Cancelled = true; if(Running) diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 5c85a2f423..8092db3663 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -19,7 +19,11 @@ class WindowBaseImpl; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; --(void) applyMenu:(NSMenu *)menu; +-(bool) isModal; +-(void) setModal: (bool) isModal; +-(void) showAppMenuOnly; +-(void) showWindowMenuWithAppMenu; +-(void) applyMenu:(NSMenu* _Nullable)menu; -(double) getScaling; @end diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 6298118c10..68899df9f0 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -27,7 +27,7 @@ public: NSObject* renderTarget; AvnPoint lastPositionSet; NSString* _lastTitle; - IAvnAppMenu* _mainMenu; + IAvnMenu* _mainMenu; bool _shown; WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) @@ -234,7 +234,7 @@ public: } } - virtual HRESULT SetMainMenu(IAvnAppMenu* menu) override + virtual HRESULT SetMainMenu(IAvnMenu* menu) override { _mainMenu = menu; @@ -244,18 +244,11 @@ public: [Window applyMenu:nsmenu]; - return S_OK; - } - - virtual HRESULT ObtainMainMenu(IAvnAppMenu** ret) override - { - if(ret == nullptr) + if ([Window isKeyWindow]) { - return E_POINTER; + [Window showWindowMenuWithAppMenu]; } - *ret = _mainMenu; - return S_OK; } @@ -432,6 +425,7 @@ private: WindowEvents = events; [Window setCanBecomeKeyAndMain]; [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; } virtual HRESULT Show () override @@ -440,6 +434,9 @@ private: { if([Window parentWindow] != nil) [[Window parentWindow] removeChildWindow:Window]; + + [Window setModal:FALSE]; + WindowBaseImpl::Show(); return SetWindowState(_lastWindowState); @@ -457,6 +454,8 @@ private: if(cparent == nullptr) return E_INVALIDARG; + [Window setModal:TRUE]; + [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; WindowBaseImpl::Show(); @@ -1151,8 +1150,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent ComPtr _parent; bool _canBecomeKeyAndMain; bool _closed; - NSMenu* _menu; - bool _isAppMenuApplied; + bool _isModal; + AvnMenu* _menu; double _lastScaling; } @@ -1189,32 +1188,64 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } --(void) applyMenu:(NSMenu *)menu +-(void) showWindowMenuWithAppMenu { - if(menu == nullptr) + if(_menu != nullptr) { - menu = [NSMenu new]; + auto appMenuItem = ::GetAppMenuItem(); + + if(appMenuItem != nullptr) + { + auto appMenu = [appMenuItem menu]; + + [appMenu removeItem:appMenuItem]; + + [_menu insertItem:appMenuItem atIndex:0]; + + [_menu setHasGlobalMenuItem:true]; + } + + [NSApp setMenu:_menu]; } +} + +-(void) showAppMenuOnly +{ + auto appMenuItem = ::GetAppMenuItem(); - _menu = menu; - - if ([self isKeyWindow]) + if(appMenuItem != nullptr) { - auto appMenu = ::GetAppMenuItem(); + auto appMenu = ::GetAppMenu(); + + auto nativeAppMenu = dynamic_cast(appMenu); - if(appMenu != nullptr) + [[appMenuItem menu] removeItem:appMenuItem]; + + if(_menu != nullptr) { - [[appMenu menu] removeItem:appMenu]; - - [_menu insertItem:appMenu atIndex:0]; - - _isAppMenuApplied = true; + [_menu setHasGlobalMenuItem:false]; } - [NSApp setMenu:menu]; + [nativeAppMenu->GetNative() addItem:appMenuItem]; + + [NSApp setMenu:nativeAppMenu->GetNative()]; + } + else + { + [NSApp setMenu:nullptr]; } } +-(void) applyMenu:(AvnMenu *)menu +{ + if(menu == nullptr) + { + menu = [AvnMenu new]; + } + + _menu = menu; +} + -(void) setCanBecomeKeyAndMain { _canBecomeKeyAndMain = true; @@ -1298,11 +1329,25 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto ch = objc_cast(uch); if(ch == nil) continue; + + if(![ch isModal]) + continue; + return FALSE; } return TRUE; } +-(bool) isModal +{ + return _isModal; +} + +-(void) setModal: (bool) isModal +{ + _isModal = isModal; +} + -(void)makeKeyWindow { if([self activateAppropriateChild: true]) @@ -1315,23 +1360,7 @@ 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]; + [self showWindowMenuWithAppMenu]; _parent->BaseEvents->Activated(); [super becomeKeyWindow]; @@ -1383,26 +1412,7 @@ 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? + [self showAppMenuOnly]; [super resignKeyWindow]; } diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 9f7593b702..2c5a09bee7 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net461;netcoreapp3.1 + netstandard2.0;net461;netcoreapp2.0 Avalonia @@ -20,8 +20,8 @@ Platform=$(Platform)" /> - <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netcoreapp3.1/Avalonia.Designer.HostApp.dll"> - tools/netcoreapp3.1/designer + <_PackageFiles Include="$(DesignerHostAppPath)/Avalonia.Designer.HostApp/bin/$(Configuration)/netcoreapp2.0/Avalonia.Designer.HostApp.dll"> + tools/netcoreapp2.0/designer false None diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props index 99d53c1e03..6f21971d3d 100644 --- a/packages/Avalonia/Avalonia.props +++ b/packages/Avalonia/Avalonia.props @@ -1,6 +1,6 @@ - $(MSBuildThisFileDirectory)\..\tools\netcoreapp3.1\designer\Avalonia.Designer.HostApp.dll + $(MSBuildThisFileDirectory)\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll $(MSBuildThisFileDirectory)\..\tools\net461\designer\Avalonia.Designer.HostApp.exe $(MSBuildThisFileDirectory)\..\tools\netstandard2.0\Avalonia.Build.Tasks.dll false diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index d25de9c1f5..bea751ad4c 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -14,9 +14,9 @@ - + - + @@ -36,6 +36,24 @@ + + + + + + + + + diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index b40fdb4a17..d97325ef8d 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -29,6 +29,7 @@ namespace ControlCatalog DataContext = new MainWindowViewModel(_notificationArea); _recentMenu = ((NativeMenu.GetMenu(this).Items[0] as NativeMenuItem).Menu.Items[2] as NativeMenuItem).Menu; + var mainMenu = this.FindControl("MainMenu"); mainMenu.AttachedToVisualTree += MenuAttached; } diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index ba68b113e3..ba1921a185 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -1,4 +1,7 @@ +using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using Avalonia.Controls; using Avalonia.Markup.Xaml; #pragma warning disable 4014 @@ -34,7 +37,9 @@ namespace ControlCatalog.Pages new OpenFileDialog() { Title = "Open file", - Filters = GetFilters() + Filters = GetFilters(), + // Almost guaranteed to exist + InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }.ShowAsync(GetWindow()); }; this.FindControl