committed by
GitHub
195 changed files with 3928 additions and 930 deletions
@ -1,5 +1,6 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> |
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> |
||||
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
{ |
{ |
||||
"msbuild-sdks": { |
"msbuild-sdks": { |
||||
"Microsoft.Build.Traversal": "1.0.43", |
"Microsoft.Build.Traversal": "1.0.43", |
||||
"MSBuild.Sdk.Extras": "1.6.65", |
"MSBuild.Sdk.Extras": "2.0.46", |
||||
"AggregatePackage.NuGet.Sdk" : "0.1.12" |
"AggregatePackage.NuGet.Sdk" : "0.1.12" |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
||||
|
<plist version="1.0"> |
||||
|
<dict> |
||||
|
<key>IDEDidComputeMac32BitWarning</key> |
||||
|
<true/> |
||||
|
</dict> |
||||
|
</plist> |
||||
@ -0,0 +1,80 @@ |
|||||
|
//
|
||||
|
// menu.h
|
||||
|
// Avalonia.Native.OSX
|
||||
|
//
|
||||
|
// Created by Dan Walmsley on 01/08/2019.
|
||||
|
// Copyright © 2019 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef menu_h |
||||
|
#define menu_h |
||||
|
|
||||
|
#include "common.h" |
||||
|
|
||||
|
class AvnAppMenuItem; |
||||
|
class AvnAppMenu; |
||||
|
|
||||
|
@interface AvnMenu : NSMenu // for some reason it doesnt detect nsmenu here but compiler doesnt complain
|
||||
|
- (void)setMenu:(NSMenu*) menu; |
||||
|
@end |
||||
|
|
||||
|
@interface AvnMenuItem : NSMenuItem |
||||
|
- (id) initWithAvnAppMenuItem: (AvnAppMenuItem*)menuItem; |
||||
|
- (void)didSelectItem:(id)sender; |
||||
|
@end |
||||
|
|
||||
|
class AvnAppMenuItem : public ComSingleObject<IAvnAppMenuItem, &IID_IAvnAppMenuItem> |
||||
|
{ |
||||
|
private: |
||||
|
NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem
|
||||
|
IAvnActionCallback* _callback; |
||||
|
IAvnPredicateCallback* _predicate; |
||||
|
bool _isSeperator; |
||||
|
|
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
|
||||
|
AvnAppMenuItem(bool isSeperator); |
||||
|
|
||||
|
NSMenuItem* GetNative(); |
||||
|
|
||||
|
virtual HRESULT SetSubMenu (IAvnAppMenu* menu) override; |
||||
|
|
||||
|
virtual HRESULT SetTitle (void* utf8String) override; |
||||
|
|
||||
|
virtual HRESULT SetGesture (void* key, AvnInputModifiers modifiers) override; |
||||
|
|
||||
|
virtual HRESULT SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) override; |
||||
|
|
||||
|
bool EvaluateItemEnabled(); |
||||
|
|
||||
|
void RaiseOnClicked(); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class AvnAppMenu : public ComSingleObject<IAvnAppMenu, &IID_IAvnAppMenu> |
||||
|
{ |
||||
|
private: |
||||
|
AvnMenu* _native; |
||||
|
|
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
|
||||
|
AvnAppMenu(); |
||||
|
|
||||
|
AvnAppMenu(AvnMenu* native); |
||||
|
|
||||
|
AvnMenu* GetNative(); |
||||
|
|
||||
|
virtual HRESULT AddItem (IAvnAppMenuItem* item) override; |
||||
|
|
||||
|
virtual HRESULT RemoveItem (IAvnAppMenuItem* item) override; |
||||
|
|
||||
|
virtual HRESULT SetTitle (void* utf8String) override; |
||||
|
|
||||
|
virtual HRESULT Clear () override; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
#endif |
||||
|
|
||||
@ -0,0 +1,307 @@ |
|||||
|
|
||||
|
#include "common.h" |
||||
|
#include "menu.h" |
||||
|
#include "window.h" |
||||
|
|
||||
|
@implementation AvnMenu |
||||
|
@end |
||||
|
|
||||
|
@implementation AvnMenuItem |
||||
|
{ |
||||
|
AvnAppMenuItem* _item; |
||||
|
} |
||||
|
|
||||
|
- (id) initWithAvnAppMenuItem: (AvnAppMenuItem*)menuItem |
||||
|
{ |
||||
|
if(self != nil) |
||||
|
{ |
||||
|
_item = menuItem; |
||||
|
self = [super initWithTitle:@"" |
||||
|
action:@selector(didSelectItem:) |
||||
|
keyEquivalent:@""]; |
||||
|
|
||||
|
[self setEnabled:YES]; |
||||
|
|
||||
|
[self setTarget:self]; |
||||
|
} |
||||
|
|
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem |
||||
|
{ |
||||
|
if([self submenu] != nil) |
||||
|
{ |
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
return _item->EvaluateItemEnabled(); |
||||
|
} |
||||
|
|
||||
|
- (void)didSelectItem:(nullable id)sender |
||||
|
{ |
||||
|
_item->RaiseOnClicked(); |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
AvnAppMenuItem::AvnAppMenuItem(bool isSeperator) |
||||
|
{ |
||||
|
_isSeperator = isSeperator; |
||||
|
|
||||
|
if(isSeperator) |
||||
|
{ |
||||
|
_native = [NSMenuItem separatorItem]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_native = [[AvnMenuItem alloc] initWithAvnAppMenuItem: this]; |
||||
|
} |
||||
|
|
||||
|
_callback = nullptr; |
||||
|
} |
||||
|
|
||||
|
NSMenuItem* AvnAppMenuItem::GetNative() |
||||
|
{ |
||||
|
return _native; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenuItem::SetSubMenu (IAvnAppMenu* menu) |
||||
|
{ |
||||
|
auto nsMenu = dynamic_cast<AvnAppMenu*>(menu)->GetNative(); |
||||
|
|
||||
|
[_native setSubmenu: nsMenu]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenuItem::SetTitle (void* utf8String) |
||||
|
{ |
||||
|
if (utf8String != nullptr) |
||||
|
{ |
||||
|
[_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenuItem::SetGesture (void* key, AvnInputModifiers modifiers) |
||||
|
{ |
||||
|
NSEventModifierFlags flags = 0; |
||||
|
|
||||
|
if (modifiers & Control) |
||||
|
flags |= NSEventModifierFlagControl; |
||||
|
if (modifiers & Shift) |
||||
|
flags |= NSEventModifierFlagShift; |
||||
|
if (modifiers & Alt) |
||||
|
flags |= NSEventModifierFlagOption; |
||||
|
if (modifiers & Windows) |
||||
|
flags |= NSEventModifierFlagCommand; |
||||
|
|
||||
|
[_native setKeyEquivalent:[NSString stringWithUTF8String:(const char*)key]]; |
||||
|
[_native setKeyEquivalentModifierMask:flags]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenuItem::SetAction (IAvnPredicateCallback* predicate, IAvnActionCallback* callback) |
||||
|
{ |
||||
|
_predicate = predicate; |
||||
|
_callback = callback; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
bool AvnAppMenuItem::EvaluateItemEnabled() |
||||
|
{ |
||||
|
if(_predicate != nullptr) |
||||
|
{ |
||||
|
auto result = _predicate->Evaluate (); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
void AvnAppMenuItem::RaiseOnClicked() |
||||
|
{ |
||||
|
if(_callback != nullptr) |
||||
|
{ |
||||
|
_callback->Run(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
AvnAppMenu::AvnAppMenu() |
||||
|
{ |
||||
|
_native = [AvnMenu new]; |
||||
|
} |
||||
|
|
||||
|
AvnAppMenu::AvnAppMenu(AvnMenu* native) |
||||
|
{ |
||||
|
_native = native; |
||||
|
} |
||||
|
|
||||
|
AvnMenu* AvnAppMenu::GetNative() |
||||
|
{ |
||||
|
return _native; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenu::AddItem (IAvnAppMenuItem* item) |
||||
|
{ |
||||
|
auto avnMenuItem = dynamic_cast<AvnAppMenuItem*>(item); |
||||
|
|
||||
|
if(avnMenuItem != nullptr) |
||||
|
{ |
||||
|
[_native addItem: avnMenuItem->GetNative()]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenu::RemoveItem (IAvnAppMenuItem* item) |
||||
|
{ |
||||
|
auto avnMenuItem = dynamic_cast<AvnAppMenuItem*>(item); |
||||
|
|
||||
|
if(avnMenuItem != nullptr) |
||||
|
{ |
||||
|
[_native removeItem:avnMenuItem->GetNative()]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenu::SetTitle (void* utf8String) |
||||
|
{ |
||||
|
if (utf8String != nullptr) |
||||
|
{ |
||||
|
[_native setTitle:[NSString stringWithUTF8String:(const char*)utf8String]]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT AvnAppMenu::Clear() |
||||
|
{ |
||||
|
[_native removeAllItems]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
extern IAvnAppMenu* CreateAppMenu() |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
id menuBar = [NSMenu new]; |
||||
|
return new AvnAppMenu(menuBar); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extern IAvnAppMenuItem* CreateAppMenuItem() |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
return new AvnAppMenuItem(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extern IAvnAppMenuItem* CreateAppMenuItemSeperator() |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
return new AvnAppMenuItem(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static IAvnAppMenu* s_appMenu = nullptr; |
||||
|
static NSMenuItem* s_appMenuItem = nullptr; |
||||
|
|
||||
|
extern void SetAppMenu (NSString* appName, IAvnAppMenu* menu) |
||||
|
{ |
||||
|
s_appMenu = menu; |
||||
|
|
||||
|
if(s_appMenu != nullptr) |
||||
|
{ |
||||
|
auto nativeMenu = dynamic_cast<AvnAppMenu*>(s_appMenu); |
||||
|
|
||||
|
auto currentMenu = [s_appMenuItem menu]; |
||||
|
|
||||
|
if (currentMenu != nullptr) |
||||
|
{ |
||||
|
[currentMenu removeItem:s_appMenuItem]; |
||||
|
} |
||||
|
|
||||
|
s_appMenuItem = [nativeMenu->GetNative() itemAtIndex:0]; |
||||
|
|
||||
|
if (currentMenu == nullptr) |
||||
|
{ |
||||
|
currentMenu = [s_appMenuItem menu]; |
||||
|
} |
||||
|
|
||||
|
[[s_appMenuItem menu] removeItem:s_appMenuItem]; |
||||
|
|
||||
|
[currentMenu insertItem:s_appMenuItem atIndex:0]; |
||||
|
|
||||
|
if([s_appMenuItem submenu] == nullptr) |
||||
|
{ |
||||
|
[s_appMenuItem setSubmenu:[NSMenu new]]; |
||||
|
} |
||||
|
|
||||
|
auto appMenu = [s_appMenuItem submenu]; |
||||
|
|
||||
|
[appMenu addItem:[NSMenuItem separatorItem]]; |
||||
|
|
||||
|
// Services item and menu |
||||
|
auto servicesItem = [[NSMenuItem alloc] init]; |
||||
|
servicesItem.title = @"Services"; |
||||
|
NSMenu *servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; |
||||
|
servicesItem.submenu = servicesMenu; |
||||
|
[NSApplication sharedApplication].servicesMenu = servicesMenu; |
||||
|
[appMenu addItem:servicesItem]; |
||||
|
|
||||
|
[appMenu addItem:[NSMenuItem separatorItem]]; |
||||
|
|
||||
|
// Hide Application |
||||
|
auto hideItem = [[NSMenuItem alloc] initWithTitle:[@"Hide " stringByAppendingString:appName] action:@selector(hide:) keyEquivalent:@"h"]; |
||||
|
|
||||
|
[appMenu addItem:hideItem]; |
||||
|
|
||||
|
// Hide Others |
||||
|
auto hideAllOthersItem = [[NSMenuItem alloc] initWithTitle:@"Hide Others" |
||||
|
action:@selector(hideOtherApplications:) |
||||
|
keyEquivalent:@"h"]; |
||||
|
|
||||
|
hideAllOthersItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagOption; |
||||
|
[appMenu addItem:hideAllOthersItem]; |
||||
|
|
||||
|
// Show All |
||||
|
auto showAllItem = [[NSMenuItem alloc] initWithTitle:@"Show All" |
||||
|
action:@selector(unhideAllApplications:) |
||||
|
keyEquivalent:@""]; |
||||
|
|
||||
|
[appMenu addItem:showAllItem]; |
||||
|
|
||||
|
[appMenu addItem:[NSMenuItem separatorItem]]; |
||||
|
|
||||
|
// Quit Application |
||||
|
auto quitItem = [[NSMenuItem alloc] init]; |
||||
|
quitItem.title = [@"Quit " stringByAppendingString:appName]; |
||||
|
quitItem.keyEquivalent = @"q"; |
||||
|
quitItem.target = [AvnWindow class]; |
||||
|
quitItem.action = @selector(closeAll); |
||||
|
[appMenu addItem:quitItem]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
s_appMenuItem = nullptr; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
extern IAvnAppMenu* GetAppMenu () |
||||
|
{ |
||||
|
return s_appMenu; |
||||
|
} |
||||
|
|
||||
|
extern NSMenuItem* GetAppMenuItem () |
||||
|
{ |
||||
|
return s_appMenuItem; |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,174 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
namespace Avalonia.Logging |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Logger sink parametrized for given logging level.
|
||||
|
/// </summary>
|
||||
|
public readonly struct ParametrizedLogger |
||||
|
{ |
||||
|
private readonly ILogSink _sink; |
||||
|
private readonly LogEventLevel _level; |
||||
|
|
||||
|
public ParametrizedLogger(ILogSink sink, LogEventLevel level) |
||||
|
{ |
||||
|
_sink = sink; |
||||
|
_level = level; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks if this logger can be used.
|
||||
|
/// </summary>
|
||||
|
public bool IsValid => _sink != null; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
/// <param name="propertyValue1">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0, T1>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0, |
||||
|
T1 propertyValue1) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
/// <param name="propertyValue1">Message property value.</param>
|
||||
|
/// <param name="propertyValue2">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0, T1, T2>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0, |
||||
|
T1 propertyValue1, |
||||
|
T2 propertyValue2) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
/// <param name="propertyValue1">Message property value.</param>
|
||||
|
/// <param name="propertyValue2">Message property value.</param>
|
||||
|
/// <param name="propertyValue3">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0, T1, T2, T3>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0, |
||||
|
T1 propertyValue1, |
||||
|
T2 propertyValue2, |
||||
|
T3 propertyValue3) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
/// <param name="propertyValue1">Message property value.</param>
|
||||
|
/// <param name="propertyValue2">Message property value.</param>
|
||||
|
/// <param name="propertyValue3">Message property value.</param>
|
||||
|
/// <param name="propertyValue4">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0, T1, T2, T3, T4>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0, |
||||
|
T1 propertyValue1, |
||||
|
T2 propertyValue2, |
||||
|
T3 propertyValue3, |
||||
|
T4 propertyValue4) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Logs an event.
|
||||
|
/// </summary>
|
||||
|
/// <param name="area">The area that the event originates.</param>
|
||||
|
/// <param name="source">The object from which the event originates.</param>
|
||||
|
/// <param name="messageTemplate">The message template.</param>
|
||||
|
/// <param name="propertyValue0">Message property value.</param>
|
||||
|
/// <param name="propertyValue1">Message property value.</param>
|
||||
|
/// <param name="propertyValue2">Message property value.</param>
|
||||
|
/// <param name="propertyValue3">Message property value.</param>
|
||||
|
/// <param name="propertyValue4">Message property value.</param>
|
||||
|
/// <param name="propertyValue5">Message property value.</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
public void Log<T0, T1, T2, T3, T4, T5>( |
||||
|
string area, |
||||
|
object source, |
||||
|
string messageTemplate, |
||||
|
T0 propertyValue0, |
||||
|
T1 propertyValue1, |
||||
|
T2 propertyValue2, |
||||
|
T3 propertyValue3, |
||||
|
T4 propertyValue4, |
||||
|
T5 propertyValue5) |
||||
|
{ |
||||
|
_sink.Log(_level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2, propertyValue3, propertyValue4, propertyValue5); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public partial class NativeMenu |
||||
|
{ |
||||
|
public static readonly AttachedProperty<bool> IsNativeMenuExportedProperty = |
||||
|
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, bool>("IsNativeMenuExported"); |
||||
|
|
||||
|
public static bool GetIsNativeMenuExported(TopLevel tl) => tl.GetValue(IsNativeMenuExportedProperty); |
||||
|
|
||||
|
private static readonly AttachedProperty<NativeMenuInfo> s_nativeMenuInfoProperty = |
||||
|
AvaloniaProperty.RegisterAttached<NativeMenu, TopLevel, NativeMenuInfo>("___NativeMenuInfo"); |
||||
|
|
||||
|
class NativeMenuInfo |
||||
|
{ |
||||
|
public bool ChangingIsExported { get; set; } |
||||
|
public ITopLevelNativeMenuExporter Exporter { get; } |
||||
|
|
||||
|
public NativeMenuInfo(TopLevel target) |
||||
|
{ |
||||
|
Exporter = (target.PlatformImpl as ITopLevelImplWithNativeMenuExporter)?.NativeMenuExporter; |
||||
|
if (Exporter != null) |
||||
|
{ |
||||
|
Exporter.OnIsNativeMenuExportedChanged += delegate |
||||
|
{ |
||||
|
SetIsNativeMenuExported(target, Exporter.IsNativeMenuExported); |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static NativeMenuInfo GetInfo(TopLevel target) |
||||
|
{ |
||||
|
var rv = target.GetValue(s_nativeMenuInfoProperty); |
||||
|
if (rv == null) |
||||
|
{ |
||||
|
target.SetValue(s_nativeMenuInfoProperty, rv = new NativeMenuInfo(target)); |
||||
|
SetIsNativeMenuExported(target, rv.Exporter?.IsNativeMenuExported ?? false); |
||||
|
} |
||||
|
|
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
static void SetIsNativeMenuExported(TopLevel tl, bool value) |
||||
|
{ |
||||
|
GetInfo(tl).ChangingIsExported = true; |
||||
|
tl.SetValue(IsNativeMenuExportedProperty, value); |
||||
|
} |
||||
|
|
||||
|
public static readonly AttachedProperty<NativeMenu> MenuProperty |
||||
|
= AvaloniaProperty.RegisterAttached<NativeMenu, AvaloniaObject, NativeMenu>("Menu", validate: |
||||
|
(o, v) => |
||||
|
{ |
||||
|
if(!(o is Application || o is TopLevel)) |
||||
|
throw new InvalidOperationException("NativeMenu.Menu property isn't valid on "+o.GetType()); |
||||
|
return v; |
||||
|
}); |
||||
|
|
||||
|
public static void SetMenu(AvaloniaObject o, NativeMenu menu) => o.SetValue(MenuProperty, menu); |
||||
|
public static NativeMenu GetMenu(AvaloniaObject o) => o.GetValue(MenuProperty); |
||||
|
|
||||
|
static NativeMenu() |
||||
|
{ |
||||
|
// This is needed because of the lack of attached direct properties
|
||||
|
IsNativeMenuExportedProperty.Changed.Subscribe(args => |
||||
|
{ |
||||
|
var info = GetInfo((TopLevel)args.Sender); |
||||
|
if (!info.ChangingIsExported) |
||||
|
throw new InvalidOperationException("IsNativeMenuExported property is read-only"); |
||||
|
info.ChangingIsExported = false; |
||||
|
}); |
||||
|
MenuProperty.Changed.Subscribe(args => |
||||
|
{ |
||||
|
if (args.Sender is TopLevel tl) |
||||
|
{ |
||||
|
GetInfo(tl).Exporter?.SetNativeMenu((NativeMenu)args.NewValue); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using Avalonia.Collections; |
||||
|
using Avalonia.Data; |
||||
|
using Avalonia.LogicalTree; |
||||
|
using Avalonia.Metadata; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public partial class NativeMenu : AvaloniaObject, IEnumerable<NativeMenuItemBase> |
||||
|
{ |
||||
|
private readonly AvaloniaList<NativeMenuItemBase> _items = |
||||
|
new AvaloniaList<NativeMenuItemBase> { ResetBehavior = ResetBehavior.Remove }; |
||||
|
private NativeMenuItem _parent; |
||||
|
[Content] |
||||
|
public IList<NativeMenuItemBase> Items => _items; |
||||
|
|
||||
|
public NativeMenu() |
||||
|
{ |
||||
|
_items.Validate = Validator; |
||||
|
_items.CollectionChanged += ItemsChanged; |
||||
|
} |
||||
|
|
||||
|
private void Validator(NativeMenuItemBase obj) |
||||
|
{ |
||||
|
if (obj.Parent != null) |
||||
|
throw new InvalidOperationException("NativeMenuItem already has a parent"); |
||||
|
} |
||||
|
|
||||
|
private void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
if(e.OldItems!=null) |
||||
|
foreach (NativeMenuItemBase i in e.OldItems) |
||||
|
i.Parent = null; |
||||
|
if(e.NewItems!=null) |
||||
|
foreach (NativeMenuItemBase i in e.NewItems) |
||||
|
i.Parent = this; |
||||
|
} |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenu, NativeMenuItem> ParentProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenu, NativeMenuItem>("Parent", o => o.Parent, (o, v) => o.Parent = v); |
||||
|
|
||||
|
public NativeMenuItem Parent |
||||
|
{ |
||||
|
get => _parent; |
||||
|
set => SetAndRaise(ParentProperty, ref _parent, value); |
||||
|
} |
||||
|
|
||||
|
public void Add(NativeMenuItemBase item) => _items.Add(item); |
||||
|
|
||||
|
public IEnumerator<NativeMenuItemBase> GetEnumerator() => _items.GetEnumerator(); |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
{ |
||||
|
return GetEnumerator(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Styling; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class NativeMenuBar : TemplatedControl |
||||
|
{ |
||||
|
public static readonly AttachedProperty<bool> EnableMenuItemClickForwardingProperty = |
||||
|
AvaloniaProperty.RegisterAttached<NativeMenuBar, MenuItem, Boolean>( |
||||
|
"EnableMenuItemClickForwarding"); |
||||
|
|
||||
|
static NativeMenuBar() |
||||
|
{ |
||||
|
EnableMenuItemClickForwardingProperty.Changed.Subscribe(args => |
||||
|
{ |
||||
|
var item = (MenuItem)args.Sender; |
||||
|
if (args.NewValue.Equals(true)) |
||||
|
item.Click += OnMenuItemClick; |
||||
|
else |
||||
|
item.Click -= OnMenuItemClick; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static void SetEnableMenuItemClickForwarding(MenuItem menuItem, bool enable) |
||||
|
{ |
||||
|
menuItem.SetValue(EnableMenuItemClickForwardingProperty, enable); |
||||
|
} |
||||
|
|
||||
|
private static void OnMenuItemClick(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
(((MenuItem)sender).DataContext as NativeMenuItem)?.RaiseClick(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
using System; |
||||
|
using System.Windows.Input; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class NativeMenuItem : NativeMenuItemBase |
||||
|
{ |
||||
|
private string _header; |
||||
|
private KeyGesture _gesture; |
||||
|
private bool _enabled = true; |
||||
|
|
||||
|
private NativeMenu _menu; |
||||
|
|
||||
|
static NativeMenuItem() |
||||
|
{ |
||||
|
MenuProperty.Changed.Subscribe(args => |
||||
|
{ |
||||
|
var item = (NativeMenuItem)args.Sender; |
||||
|
var value = (NativeMenu)args.NewValue; |
||||
|
if (value.Parent != null && value.Parent != item) |
||||
|
throw new InvalidOperationException("NativeMenu already has a parent"); |
||||
|
value.Parent = item; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class CanExecuteChangedSubscriber : IWeakSubscriber<EventArgs> |
||||
|
{ |
||||
|
private readonly NativeMenuItem _parent; |
||||
|
|
||||
|
public CanExecuteChangedSubscriber(NativeMenuItem parent) |
||||
|
{ |
||||
|
_parent = parent; |
||||
|
} |
||||
|
|
||||
|
public void OnEvent(object sender, EventArgs e) |
||||
|
{ |
||||
|
_parent.CanExecuteChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly CanExecuteChangedSubscriber _canExecuteChangedSubscriber; |
||||
|
|
||||
|
|
||||
|
public NativeMenuItem() |
||||
|
{ |
||||
|
_canExecuteChangedSubscriber = new CanExecuteChangedSubscriber(this); |
||||
|
} |
||||
|
|
||||
|
public NativeMenuItem(string header) : this() |
||||
|
{ |
||||
|
Header = header; |
||||
|
} |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, NativeMenu> MenuProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>(nameof(Menu), o => o._menu, |
||||
|
(o, v) => |
||||
|
{ |
||||
|
if (v.Parent != null && v.Parent != o) |
||||
|
throw new InvalidOperationException("NativeMenu already has a parent"); |
||||
|
o._menu = v; |
||||
|
}); |
||||
|
|
||||
|
public NativeMenu Menu |
||||
|
{ |
||||
|
get => _menu; |
||||
|
set |
||||
|
{ |
||||
|
if (value.Parent != null && value.Parent != this) |
||||
|
throw new InvalidOperationException("NativeMenu already has a parent"); |
||||
|
SetAndRaise(MenuProperty, ref _menu, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, string> HeaderProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, string>(nameof(Header), o => o._header, (o, v) => o._header = v); |
||||
|
|
||||
|
public string Header |
||||
|
{ |
||||
|
get => GetValue(HeaderProperty); |
||||
|
set => SetValue(HeaderProperty, value); |
||||
|
} |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, KeyGesture> GestureProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, KeyGesture>(nameof(Gesture), o => o._gesture, (o, v) => o._gesture = v); |
||||
|
|
||||
|
public KeyGesture Gesture |
||||
|
{ |
||||
|
get => GetValue(GestureProperty); |
||||
|
set => SetValue(GestureProperty, value); |
||||
|
} |
||||
|
|
||||
|
private ICommand _command; |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, ICommand> CommandProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, ICommand>(nameof(Command), |
||||
|
o => o._command, (o, v) => |
||||
|
{ |
||||
|
if (o._command != null) |
||||
|
WeakSubscriptionManager.Unsubscribe(o._command, |
||||
|
nameof(ICommand.CanExecuteChanged), o._canExecuteChangedSubscriber); |
||||
|
o._command = v; |
||||
|
if (o._command != null) |
||||
|
WeakSubscriptionManager.Subscribe(o._command, |
||||
|
nameof(ICommand.CanExecuteChanged), o._canExecuteChangedSubscriber); |
||||
|
o.CanExecuteChanged(); |
||||
|
}); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="CommandParameter"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<object> CommandParameterProperty = |
||||
|
Button.CommandParameterProperty.AddOwner<MenuItem>(); |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, bool> EnabledProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, bool>(nameof(Enabled), o => o._enabled, |
||||
|
(o, v) => o._enabled = v, true); |
||||
|
|
||||
|
public bool Enabled |
||||
|
{ |
||||
|
get => GetValue(EnabledProperty); |
||||
|
set => SetValue(EnabledProperty, value); |
||||
|
} |
||||
|
|
||||
|
void CanExecuteChanged() |
||||
|
{ |
||||
|
Enabled = _command?.CanExecute(null) ?? true; |
||||
|
} |
||||
|
|
||||
|
public bool HasClickHandlers => Clicked != null; |
||||
|
|
||||
|
public ICommand Command |
||||
|
{ |
||||
|
get => GetValue(CommandProperty); |
||||
|
set => SetValue(CommandProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the parameter to pass to the <see cref="Command"/> property of a
|
||||
|
/// <see cref="NativeMenuItem"/>.
|
||||
|
/// </summary>
|
||||
|
public object CommandParameter |
||||
|
{ |
||||
|
get { return GetValue(CommandParameterProperty); } |
||||
|
set { SetValue(CommandParameterProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public event EventHandler Clicked; |
||||
|
|
||||
|
public void RaiseClick() |
||||
|
{ |
||||
|
Clicked?.Invoke(this, new EventArgs()); |
||||
|
|
||||
|
if (Command?.CanExecute(CommandParameter) == true) |
||||
|
{ |
||||
|
Command.Execute(CommandParameter); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class NativeMenuItemBase : AvaloniaObject |
||||
|
{ |
||||
|
private NativeMenu _parent; |
||||
|
|
||||
|
internal NativeMenuItemBase() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static readonly DirectProperty<NativeMenuItem, NativeMenu> ParentProperty = |
||||
|
AvaloniaProperty.RegisterDirect<NativeMenuItem, NativeMenu>("Parent", o => o.Parent, (o, v) => o.Parent = v); |
||||
|
|
||||
|
public NativeMenu Parent |
||||
|
{ |
||||
|
get => _parent; |
||||
|
set => SetAndRaise(ParentProperty, ref _parent, value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class NativeMenuItemSeperator : NativeMenuItemBase |
||||
|
{ |
||||
|
[Obsolete("This is a temporary hack to make our MenuItem recognize this as a separator, don't use", true)] |
||||
|
public string Header => "-"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Controls.Platform |
||||
|
{ |
||||
|
public interface ITopLevelNativeMenuExporter |
||||
|
{ |
||||
|
bool IsNativeMenuExported { get; } |
||||
|
event EventHandler OnIsNativeMenuExportedChanged; |
||||
|
void SetNativeMenu(NativeMenu menu); |
||||
|
} |
||||
|
|
||||
|
public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl |
||||
|
{ |
||||
|
ITopLevelNativeMenuExporter NativeMenuExporter { get; } |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue