diff --git a/Avalonia.sln b/Avalonia.sln
index 38f8e5f720..e8d5034fb0 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -206,6 +206,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -1921,6 +1923,30 @@ Global
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhone.Build.0 = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Documentation/build.md b/Documentation/build.md
index 56b028206d..8c2ef57b54 100644
--- a/Documentation/build.md
+++ b/Documentation/build.md
@@ -36,7 +36,7 @@ Avalonia requires [CastXML](https://github.com/CastXML/CastXML) for XML processi
On macOS:
```
-brew install castxml
+brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/8a004a91a7fcd3f6620d5b01b6541ff0a640ffba/Formula/castxml.rb
```
On Debian based Linux (Debian, Ubuntu, Mint, etc):
diff --git a/build.sh b/build.sh
index 40b1c225a6..a40e00f815 100755
--- a/build.sh
+++ b/build.sh
@@ -67,6 +67,8 @@ else
fi
fi
+export PATH=$DOTNET_DIRECTORY:$PATH
+
echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" -- ${BUILD_ARGUMENTS[@]}
diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props
index d61268173d..2b54ee3f56 100644
--- a/build/CoreLibraries.props
+++ b/build/CoreLibraries.props
@@ -11,6 +11,7 @@
+
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index 08a9aa3ceb..4def44cbd0 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..f9bfaf0b47 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,12 @@ struct IAvnGlContext;
struct IAvnGlDisplay;
struct IAvnGlSurfaceRenderTarget;
struct IAvnGlSurfaceRenderingSession;
-struct IAvnAppMenu;
-struct IAvnAppMenuItem;
+struct IAvnMenu;
+struct IAvnMenuItem;
+struct IAvnStringArray;
+struct IAvnDndResultCallback;
+struct IAvnGCHandleDeallocatorCallback;
+struct IAvnMenuEvents;
enum SystemDecorations {
SystemDecorationsNone = 0,
@@ -128,11 +133,28 @@ enum AvnInputModifiers
XButton2MouseButton = 256
};
+enum class AvnDragDropEffects
+{
+ None = 0,
+ Copy = 1,
+ Move = 2,
+ Link = 4,
+};
+
+enum class AvnDragEventType
+{
+ Enter,
+ Over,
+ Leave,
+ Drop
+};
+
enum AvnWindowState
{
Normal,
Minimized,
Maximized,
+ FullScreen,
};
enum AvnStandardCursorType
@@ -175,10 +197,17 @@ enum AvnWindowEdge
WindowEdgeSouthEast
};
+enum AvnMenuItemToggleType
+{
+ None,
+ CheckMark,
+ Radio
+};
+
AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
{
public:
- virtual HRESULT Initialize() = 0;
+ virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) = 0;
virtual IAvnMacOptions* GetMacOptions() = 0;
virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) = 0;
virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) = 0;
@@ -186,13 +215,13 @@ public:
virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
+ virtual HRESULT CreateDndClipboard(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,12 +251,14 @@ 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;
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
+ virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
+ IAvnClipboard* clipboard, IAvnDndResultCallback* cb, void* sourceHandle) = 0;
+ virtual HRESULT SetBlurEnabled (bool enable) = 0;
};
AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
@@ -237,9 +268,10 @@ AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
{
- virtual HRESULT ShowDialog (IAvnWindow* parent) = 0;
+ virtual HRESULT SetEnabled (bool enable) = 0;
+ virtual HRESULT SetParent (IAvnWindow* parent) = 0;
virtual HRESULT SetCanResize(bool value) = 0;
- virtual HRESULT SetHasDecorations(SystemDecorations value) = 0;
+ virtual HRESULT SetDecorations(SystemDecorations value) = 0;
virtual HRESULT SetTitle (void* utf8Title) = 0;
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;
@@ -263,6 +295,9 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
virtual void ScalingChanged(double scaling) = 0;
virtual void RunRenderPriorityJobs() = 0;
+ virtual AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
+ AvnInputModifiers modifiers, AvnDragDropEffects effects,
+ IAvnClipboard* clipboard, void* dataObjectHandle) = 0;
};
@@ -276,6 +311,8 @@ AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
virtual bool Closing () = 0;
virtual void WindowStateChanged (AvnWindowState state) = 0;
+
+ virtual void GotInputWhenDisabled () = 0;
};
AVNCOM(IAvnMacOptions, 07) : IUnknown
@@ -346,8 +383,13 @@ AVNCOM(IAvnScreens, 0e) : IUnknown
AVNCOM(IAvnClipboard, 0f) : IUnknown
{
- virtual HRESULT GetText (IAvnString**ppv) = 0;
- virtual HRESULT SetText (void* utf8Text) = 0;
+ virtual HRESULT GetText (char* type, IAvnString**ppv) = 0;
+ virtual HRESULT SetText (char* type, void* utf8Text) = 0;
+ virtual HRESULT ObtainFormats(IAvnStringArray**ppv) = 0;
+ virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) = 0;
+ virtual HRESULT SetBytes(char* type, void* utf8Text, int len) = 0;
+ virtual HRESULT GetBytes(char* type, IAvnString**ppv) = 0;
+
virtual HRESULT Clear() = 0;
};
@@ -388,10 +430,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 +443,39 @@ 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;
+};
+
+AVNCOM(IAvnStringArray, 20) : IUnknown
+{
+ virtual unsigned int GetCount() = 0;
+ virtual HRESULT Get(unsigned int index, IAvnString**ppv) = 0;
+};
+
+AVNCOM(IAvnDndResultCallback, 21) : IUnknown
+{
+ virtual void OnDragAndDropComplete(AvnDragDropEffects effecct) = 0;
+};
+
+AVNCOM(IAvnGCHandleDeallocatorCallback, 22) : IUnknown
+{
+ virtual void FreeGCHandle(void* handle) = 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 50a85bdf9f..ea28780c81 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
@@ -12,6 +12,7 @@
1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
+ 1A465D10246AB61600C5858B /* dnd.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A465D0F246AB61600C5858B /* dnd.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 */; };
@@ -33,6 +34,7 @@
1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = ""; };
1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+ 1A465D0F246AB61600C5858B /* dnd.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = dnd.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 = ""; };
@@ -92,6 +94,7 @@
5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
+ 1A465D0F246AB61600C5858B /* dnd.mm */,
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
AB661C212148288600291242 /* common.h */,
379860FE214DA0C000CD0246 /* KeyTransform.h */,
@@ -196,6 +199,7 @@
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
+ 1A465D10246AB61600C5858B /* dnd.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
AB661C202148286E00291242 /* window.mm in Sources */,
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">
-
-
-
-
* array);
+extern IAvnStringArray* CreateAvnStringArray(NSString* string);
+extern IAvnString* CreateByteArray(void* data, int len);
#endif /* AvnString_h */
diff --git a/native/Avalonia.Native/src/OSX/AvnString.mm b/native/Avalonia.Native/src/OSX/AvnString.mm
index b62fe8a968..00b748ef63 100644
--- a/native/Avalonia.Native/src/OSX/AvnString.mm
+++ b/native/Avalonia.Native/src/OSX/AvnString.mm
@@ -7,6 +7,7 @@
//
#include "common.h"
+#include
class AvnStringImpl : public virtual ComSingleObject
{
@@ -28,6 +29,13 @@ public:
memcpy((void*)_cstring, (void*)cstring, _length);
}
+ AvnStringImpl(void*ptr, int len)
+ {
+ _length = len;
+ _cstring = (const char*)malloc(_length);
+ memcpy((void*)_cstring, ptr, len);
+ }
+
virtual ~AvnStringImpl()
{
free((void*)_cstring);
@@ -61,7 +69,60 @@ public:
}
};
+class AvnStringArrayImpl : public virtual ComSingleObject
+{
+private:
+ std::vector> _list;
+public:
+ FORWARD_IUNKNOWN()
+ AvnStringArrayImpl(NSArray* array)
+ {
+ for(int c = 0; c < [array count]; c++)
+ {
+ ComPtr s;
+ *s.getPPV() = new AvnStringImpl([array objectAtIndex:c]);
+ _list.push_back(s);
+ }
+ }
+
+ AvnStringArrayImpl(NSString* string)
+ {
+ ComPtr s;
+ *s.getPPV() = new AvnStringImpl(string);
+ _list.push_back(s);
+ }
+
+ virtual unsigned int GetCount() override
+ {
+ return (unsigned int)_list.size();
+ }
+
+ virtual HRESULT Get(unsigned int index, IAvnString**ppv) override
+ {
+ if(_list.size() <= index)
+ return E_INVALIDARG;
+ *ppv = _list[index].getRetainedReference();
+ return S_OK;
+ }
+};
+
IAvnString* CreateAvnString(NSString* string)
{
return new AvnStringImpl(string);
}
+
+
+IAvnStringArray* CreateAvnStringArray(NSArray * array)
+{
+ return new AvnStringArrayImpl(array);
+}
+
+IAvnStringArray* CreateAvnStringArray(NSString* string)
+{
+ return new AvnStringArrayImpl(string);
+}
+
+IAvnString* CreateByteArray(void* data, int len)
+{
+ return new AvnStringImpl(data, len);
+}
diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
index 3e0911a4aa..a47221056b 100644
--- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm
+++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm
@@ -20,6 +20,7 @@ public:
if(title != nullptr)
{
+ panel.message = [NSString stringWithUTF8String:title];
panel.title = [NSString stringWithUTF8String:title];
}
@@ -94,6 +95,7 @@ public:
if(title != nullptr)
{
+ panel.message = [NSString stringWithUTF8String:title];
panel.title = [NSString stringWithUTF8String:title];
}
@@ -182,6 +184,7 @@ public:
if(title != nullptr)
{
+ panel.message = [NSString stringWithUTF8String:title];
panel.title = [NSString stringWithUTF8String:title];
}
diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm
index 5c50aad4cc..1e74a70e66 100644
--- a/native/Avalonia.Native/src/OSX/app.mm
+++ b/native/Avalonia.Native/src/OSX/app.mm
@@ -2,7 +2,8 @@
@interface AvnAppDelegate : NSObject
@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/clipboard.mm b/native/Avalonia.Native/src/OSX/clipboard.mm
index c2cf1f1f61..116a08670e 100644
--- a/native/Avalonia.Native/src/OSX/clipboard.mm
+++ b/native/Avalonia.Native/src/OSX/clipboard.mm
@@ -3,16 +3,27 @@
class Clipboard : public ComSingleObject
{
+private:
+ NSPasteboard* _pb;
+ NSPasteboardItem* _item;
public:
FORWARD_IUNKNOWN()
- Clipboard()
+ Clipboard(NSPasteboard* pasteboard, NSPasteboardItem* item)
{
- NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
- [pasteBoard stringForType:NSPasteboardTypeString];
+ if(pasteboard == nil && item == nil)
+ pasteboard = [NSPasteboard generalPasteboard];
+
+ _pb = pasteboard;
+ _item = item;
}
- virtual HRESULT GetText (IAvnString**ppv) override
+ NSPasteboardItem* TryGetItem()
+ {
+ return _item;
+ }
+
+ virtual HRESULT GetText (char* type, IAvnString**ppv) override
{
@autoreleasepool
{
@@ -20,39 +31,124 @@ public:
{
return E_POINTER;
}
+ NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
+ NSString* string = _item == nil ? [_pb stringForType:typeString] : [_item stringForType:typeString];
- *ppv = CreateAvnString([[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString]);
+ *ppv = CreateAvnString(string);
return S_OK;
}
}
- virtual HRESULT SetText (void* utf8String) override
+ virtual HRESULT GetStrings(char* type, IAvnStringArray**ppv) override
{
@autoreleasepool
{
- NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
- [pasteBoard clearContents];
- [pasteBoard setString:[NSString stringWithUTF8String:(const char*)utf8String] forType:NSPasteboardTypeString];
+ *ppv= nil;
+ NSString* typeString = [NSString stringWithUTF8String:(const char*)type];
+ NSObject* data = _item == nil ? [_pb propertyListForType: typeString] : [_item propertyListForType: typeString];
+ if(data == nil)
+ return S_OK;
+
+ if([data isKindOfClass: [NSString class]])
+ {
+ *ppv = CreateAvnStringArray((NSString*) data);
+ return S_OK;
+ }
+
+ NSArray* arr = (NSArray*)data;
+
+ for(int c = 0; c < [arr count]; c++)
+ if(![[arr objectAtIndex:c] isKindOfClass:[NSString class]])
+ return E_INVALIDARG;
+
+ *ppv = CreateAvnStringArray(arr);
+ return S_OK;
+ }
+ }
+
+ virtual HRESULT SetText (char* type, void* utf8String) override
+ {
+ Clear();
+ @autoreleasepool
+ {
+ auto string = [NSString stringWithUTF8String:(const char*)utf8String];
+ auto typeString = [NSString stringWithUTF8String:(const char*)type];
+ if(_item == nil)
+ [_pb setString: string forType: typeString];
+ else
+ [_item setString: string forType:typeString];
}
return S_OK;
}
+
+ virtual HRESULT SetBytes(char* type, void* bytes, int len) override
+ {
+ auto typeString = [NSString stringWithUTF8String:(const char*)type];
+ auto data = [NSData dataWithBytes:bytes length:len];
+ if(_item == nil)
+ [_pb setData:data forType:typeString];
+ else
+ [_item setData:data forType:typeString];
+ return S_OK;
+ }
+
+ virtual HRESULT GetBytes(char* type, IAvnString**ppv) override
+ {
+ *ppv = nil;
+ auto typeString = [NSString stringWithUTF8String:(const char*)type];
+ NSData*data;
+ @try
+ {
+ if(_item)
+ data = [_item dataForType:typeString];
+ else
+ data = [_pb dataForType:typeString];
+ if(data == nil)
+ return E_FAIL;
+ }
+ @catch(NSException* e)
+ {
+ return E_FAIL;
+ }
+ *ppv = CreateByteArray((void*)data.bytes, (int)data.length);
+ return S_OK;
+ }
+
virtual HRESULT Clear() override
{
@autoreleasepool
{
- NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
- [pasteBoard clearContents];
- [pasteBoard setString:@"" forType:NSPasteboardTypeString];
+ if(_item != nil)
+ _item = [NSPasteboardItem new];
+ else
+ {
+ [_pb clearContents];
+ [_pb setString:@"" forType:NSPasteboardTypeString];
+ }
}
return S_OK;
}
+
+ virtual HRESULT ObtainFormats(IAvnStringArray** ppv) override
+ {
+ *ppv = CreateAvnStringArray(_item == nil ? [_pb types] : [_item types]);
+ return S_OK;
+ }
};
-extern IAvnClipboard* CreateClipboard()
+extern IAvnClipboard* CreateClipboard(NSPasteboard* pb, NSPasteboardItem* item)
+{
+ return new Clipboard(pb, item);
+}
+
+extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*cb)
{
- return new Clipboard();
+ auto clipboard = dynamic_cast(cb);
+ if(clipboard == nil)
+ return nil;
+ return clipboard->TryGetItem();
}
diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h
index 85403abfe7..df6a7be91c 100644
--- a/native/Avalonia.Native/src/OSX/common.h
+++ b/native/Avalonia.Native/src/OSX/common.h
@@ -8,18 +8,24 @@
#include
extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
+extern void FreeAvnGCHandle(void* handle);
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl);
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl);
extern IAvnSystemDialogs* CreateSystemDialogs();
extern IAvnScreens* CreateScreens();
-extern IAvnClipboard* CreateClipboard();
+extern IAvnClipboard* CreateClipboard(NSPasteboard*, NSPasteboardItem*);
+extern NSPasteboardItem* TryGetPasteboardItem(IAvnClipboard*);
+extern NSObject* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle);
+extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject* info);
+extern NSString* GetAvnCustomDataType();
+extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop);
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/dnd.mm b/native/Avalonia.Native/src/OSX/dnd.mm
new file mode 100644
index 0000000000..294b8ee8ea
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/dnd.mm
@@ -0,0 +1,89 @@
+#include "common.h"
+
+extern AvnDragDropEffects ConvertDragDropEffects(NSDragOperation nsop)
+{
+ int effects = 0;
+ if((nsop & NSDragOperationCopy) != 0)
+ effects |= (int)AvnDragDropEffects::Copy;
+ if((nsop & NSDragOperationMove) != 0)
+ effects |= (int)AvnDragDropEffects::Move;
+ if((nsop & NSDragOperationLink) != 0)
+ effects |= (int)AvnDragDropEffects::Link;
+ return (AvnDragDropEffects)effects;
+};
+
+extern NSString* GetAvnCustomDataType()
+{
+ char buffer[256];
+ sprintf(buffer, "net.avaloniaui.inproc.uti.n%in", getpid());
+ return [NSString stringWithUTF8String:buffer];
+}
+
+@interface AvnDndSource : NSObject
+
+@end
+
+@implementation AvnDndSource
+{
+ NSDragOperation _operation;
+ ComPtr _cb;
+ void* _sourceHandle;
+};
+
+- (NSDragOperation)draggingSession:(nonnull NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+{
+ return NSDragOperationCopy;
+}
+
+- (AvnDndSource*) initWithOperation: (NSDragOperation)operation
+ andCallback: (IAvnDndResultCallback*) cb
+ andSourceHandle: (void*) handle
+{
+ self = [super init];
+ _operation = operation;
+ _cb = cb;
+ _sourceHandle = handle;
+ return self;
+}
+
+- (void)draggingSession:(NSDraggingSession *)session
+ endedAtPoint:(NSPoint)screenPoint
+ operation:(NSDragOperation)operation
+{
+ if(_cb != nil)
+ {
+ auto cb = _cb;
+ _cb = nil;
+ cb->OnDragAndDropComplete(ConvertDragDropEffects(operation));
+ }
+ if(_sourceHandle != nil)
+ {
+ FreeAvnGCHandle(_sourceHandle);
+ _sourceHandle = nil;
+ }
+}
+
+- (void*) gcHandle
+{
+ return _sourceHandle;
+}
+
+@end
+
+extern NSObject* CreateDraggingSource(NSDragOperation op, IAvnDndResultCallback* cb, void* handle)
+{
+ return [[AvnDndSource alloc] initWithOperation:op andCallback:cb andSourceHandle:handle];
+};
+
+extern void* GetAvnDataObjectHandleFromDraggingInfo(NSObject* info)
+{
+ id obj = [info draggingSource];
+ if(obj == nil)
+ return nil;
+ if([obj isKindOfClass: [AvnDndSource class]])
+ {
+ auto src = (AvnDndSource*)obj;
+ return [src gcHandle];
+ }
+ return nil;
+}
diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm
index a2134de6c1..e6c4a861fd 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
@@ -151,14 +150,15 @@ public:
}
@end
-
+static ComPtr _deallocator;
class AvaloniaNative : public ComSingleObject
{
public:
FORWARD_IUNKNOWN()
- virtual HRESULT Initialize() override
+ virtual HRESULT Initialize(IAvnGCHandleDeallocatorCallback* deallocator) override
{
+ _deallocator = deallocator;
@autoreleasepool{
[[ThreadingInitializer new] do];
}
@@ -208,7 +208,13 @@ public:
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) override
{
- *ppv = ::CreateClipboard ();
+ *ppv = ::CreateClipboard (nil, nil);
+ return S_OK;
+ }
+
+ virtual HRESULT CreateDndClipboard(IAvnClipboard** ppv) override
+ {
+ *ppv = ::CreateClipboard (nil, [NSPasteboardItem new]);
return S_OK;
}
@@ -228,41 +234,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()
@@ -270,6 +264,12 @@ extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
return new AvaloniaNative();
};
+extern void FreeAvnGCHandle(void* handle)
+{
+ if(_deallocator != nil)
+ _deallocator->FreeGCHandle(handle);
+}
+
NSSize ToNSSize (AvnSize s)
{
NSSize result;
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..bdf3007a28 100644
--- a/native/Avalonia.Native/src/OSX/window.h
+++ b/native/Avalonia.Native/src/OSX/window.h
@@ -3,7 +3,10 @@
class WindowBaseImpl;
-@interface AvnView : NSView
+@interface AutoFitContentVisualEffectView : NSVisualEffectView
+@end
+
+@interface AvnView : NSView
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent;
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
@@ -19,7 +22,10 @@ class WindowBaseImpl;
-(void) pollModalSession: (NSModalSession _Nonnull) session;
-(void) restoreParentWindow;
-(bool) shouldTryToHandleEvents;
--(void) applyMenu:(NSMenu *)menu;
+-(void) setEnabled: (bool) enable;
+-(void) showAppMenuOnly;
+-(void) showWindowMenuWithAppMenu;
+-(void) applyMenu:(NSMenu* _Nullable)menu;
-(double) getScaling;
@end
@@ -31,6 +37,10 @@ struct INSWindowHolder
struct IWindowStateChanged
{
virtual void WindowStateChanged () = 0;
+ virtual void StartStateTransition () = 0;
+ virtual void EndStateTransition () = 0;
+ virtual SystemDecorations Decorations () = 0;
+ virtual AvnWindowState WindowState () = 0;
};
#endif /* window_h */
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 6298118c10..abfae3cf1e 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -20,6 +20,7 @@ public:
View = NULL;
Window = NULL;
}
+ NSVisualEffectView* VisualEffect;
AvnView* View;
AvnWindow* Window;
ComPtr BaseEvents;
@@ -27,7 +28,7 @@ public:
NSObject* renderTarget;
AvnPoint lastPositionSet;
NSString* _lastTitle;
- IAvnAppMenu* _mainMenu;
+ IAvnMenu* _mainMenu;
bool _shown;
WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl)
@@ -47,6 +48,12 @@ public:
[Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered];
+
+ VisualEffect = [AutoFitContentVisualEffectView new];
+ [VisualEffect setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
+ [VisualEffect setMaterial:NSVisualEffectMaterialLight];
+ [VisualEffect setAutoresizesSubviews:true];
+
[Window setContentView: View];
}
@@ -234,7 +241,7 @@ public:
}
}
- virtual HRESULT SetMainMenu(IAvnAppMenu* menu) override
+ virtual HRESULT SetMainMenu(IAvnMenu* menu) override
{
_mainMenu = menu;
@@ -244,18 +251,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;
}
@@ -389,6 +389,62 @@ public:
*ppv = [renderTarget createSurfaceRenderTarget];
return *ppv == nil ? E_FAIL : S_OK;
}
+
+ virtual HRESULT SetBlurEnabled (bool enable) override
+ {
+ [Window setContentView: enable ? VisualEffect : View];
+
+ if(enable)
+ {
+ [VisualEffect addSubview:View];
+ }
+
+ return S_OK;
+ }
+
+ virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point,
+ IAvnClipboard* clipboard, IAvnDndResultCallback* cb,
+ void* sourceHandle) override
+ {
+ auto item = TryGetPasteboardItem(clipboard);
+ [item setString:@"" forType:GetAvnCustomDataType()];
+ if(item == nil)
+ return E_INVALIDARG;
+ if(View == NULL)
+ return E_FAIL;
+
+ auto nsevent = [NSApp currentEvent];
+ auto nseventType = [nsevent type];
+
+ // If current event isn't a mouse one (probably due to malfunctioning user app)
+ // attempt to forge a new one
+ if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited)
+ || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged)))
+ {
+ auto nspoint = [Window convertBaseToScreen: ToNSPoint(point)];
+ CGPoint cgpoint = NSPointToCGPoint(nspoint);
+ auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft);
+ nsevent = [NSEvent eventWithCGEvent: cgevent];
+ CFRelease(cgevent);
+ }
+
+ auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item];
+
+ auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments];
+ NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height};
+ [dragItem setDraggingFrame: dragItemRect contents: dragItemImage];
+
+ int op = 0; int ieffects = (int)effects;
+ if((ieffects & (int)AvnDragDropEffects::Copy) != 0)
+ op |= NSDragOperationCopy;
+ if((ieffects & (int)AvnDragDropEffects::Link) != 0)
+ op |= NSDragOperationLink;
+ if((ieffects & (int)AvnDragDropEffects::Move) != 0)
+ op |= NSDragOperationMove;
+ [View beginDraggingSessionWithItems: @[dragItem] event: nsevent
+ source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)];
+ return S_OK;
+ }
protected:
virtual NSWindowStyleMask GetStyle()
@@ -398,7 +454,7 @@ protected:
void UpdateStyle()
{
- [Window setStyleMask:GetStyle()];
+ [Window setStyleMask: GetStyle()];
}
public:
@@ -411,10 +467,13 @@ public:
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged
{
private:
- bool _canResize = true;
- SystemDecorations _hasDecorations = SystemDecorationsFull;
- CGRect _lastUndecoratedFrame;
+ bool _canResize;
+ bool _fullScreenActive;
+ SystemDecorations _decorations;
AvnWindowState _lastWindowState;
+ bool _inSetWindowState;
+ NSRect _preZoomSize;
+ bool _transitioningWindowState;
FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP()
@@ -428,25 +487,54 @@ private:
ComPtr WindowEvents;
WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
{
+ _fullScreenActive = false;
+ _canResize = true;
+ _decorations = SystemDecorationsFull;
+ _transitioningWindowState = false;
+ _inSetWindowState = false;
_lastWindowState = Normal;
WindowEvents = events;
[Window setCanBecomeKeyAndMain];
[Window disableCursorRects];
+ [Window setTabbingMode:NSWindowTabbingModeDisallowed];
+ }
+
+ void HideOrShowTrafficLights ()
+ {
+ for (id subview in Window.contentView.superview.subviews) {
+ if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
+ NSView *titlebarView = [subview subviews][0];
+ for (id button in titlebarView.subviews) {
+ if ([button isKindOfClass:[NSButton class]]) {
+ [button setHidden: (_decorations != SystemDecorationsFull)];
+ }
+ }
+ }
+ }
}
virtual HRESULT Show () override
{
@autoreleasepool
- {
- if([Window parentWindow] != nil)
- [[Window parentWindow] removeChildWindow:Window];
+ {
WindowBaseImpl::Show();
+ HideOrShowTrafficLights();
+
return SetWindowState(_lastWindowState);
}
}
- virtual HRESULT ShowDialog (IAvnWindow* parent) override
+ virtual HRESULT SetEnabled (bool enable) override
+ {
+ @autoreleasepool
+ {
+ [Window setEnabled:enable];
+ return S_OK;
+ }
+ }
+
+ virtual HRESULT SetParent (IAvnWindow* parent) override
{
@autoreleasepool
{
@@ -458,43 +546,70 @@ private:
return E_INVALIDARG;
[cparent->Window addChildWindow:Window ordered:NSWindowAbove];
- WindowBaseImpl::Show();
+
+ UpdateStyle();
return S_OK;
}
}
+ void StartStateTransition () override
+ {
+ _transitioningWindowState = true;
+ }
+
+ void EndStateTransition () override
+ {
+ _transitioningWindowState = false;
+ }
+
+ SystemDecorations Decorations () override
+ {
+ return _decorations;
+ }
+
+ AvnWindowState WindowState () override
+ {
+ return _lastWindowState;
+ }
+
void WindowStateChanged () override
{
- AvnWindowState state;
- GetWindowState(&state);
- WindowEvents->WindowStateChanged(state);
+ if(!_inSetWindowState && !_transitioningWindowState)
+ {
+ AvnWindowState state;
+ GetWindowState(&state);
+
+ if(_lastWindowState != state)
+ {
+ _lastWindowState = state;
+ WindowEvents->WindowStateChanged(state);
+ }
+ }
}
bool UndecoratedIsMaximized ()
{
- return CGRectEqualToRect([Window frame], [Window screen].visibleFrame);
+ auto windowSize = [Window frame];
+ auto available = [Window screen].visibleFrame;
+ return CGRectEqualToRect(windowSize, available);
}
bool IsZoomed ()
{
- return _hasDecorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized();
+ return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized();
}
void DoZoom()
{
- switch (_hasDecorations)
+ switch (_decorations)
{
case SystemDecorationsNone:
- if (!UndecoratedIsMaximized())
- {
- _lastUndecoratedFrame = [Window frame];
- }
-
- [Window zoom:Window];
+ case SystemDecorationsBorderOnly:
+ [Window setFrame:[Window screen].visibleFrame display:true];
break;
- case SystemDecorationsBorderOnly:
+
case SystemDecorationsFull:
[Window performZoom:Window];
break;
@@ -511,25 +626,52 @@ private:
}
}
- virtual HRESULT SetHasDecorations(SystemDecorations value) override
+ virtual HRESULT SetDecorations(SystemDecorations value) override
{
@autoreleasepool
{
- _hasDecorations = value;
+ auto currentWindowState = _lastWindowState;
+ _decorations = value;
+
+ if(_fullScreenActive)
+ {
+ return S_OK;
+ }
+
+ auto currentFrame = [Window frame];
+
UpdateStyle();
+
+ HideOrShowTrafficLights();
- switch (_hasDecorations)
+ switch (_decorations)
{
case SystemDecorationsNone:
[Window setHasShadow:NO];
[Window setTitleVisibility:NSWindowTitleHidden];
[Window setTitlebarAppearsTransparent:YES];
+
+ if(currentWindowState == Maximized)
+ {
+ if(!UndecoratedIsMaximized())
+ {
+ DoZoom();
+ }
+ }
break;
case SystemDecorationsBorderOnly:
[Window setHasShadow:YES];
[Window setTitleVisibility:NSWindowTitleHidden];
[Window setTitlebarAppearsTransparent:YES];
+
+ if(currentWindowState == Maximized)
+ {
+ if(!UndecoratedIsMaximized())
+ {
+ DoZoom();
+ }
+ }
break;
case SystemDecorationsFull:
@@ -537,6 +679,13 @@ private:
[Window setTitleVisibility:NSWindowTitleVisible];
[Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
+
+ if(currentWindowState == Maximized)
+ {
+ auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+ [View setFrameSize:newFrame];
+ }
break;
}
@@ -593,13 +742,19 @@ private:
return E_POINTER;
}
+ if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen)
+ {
+ *ret = FullScreen;
+ return S_OK;
+ }
+
if([Window isMiniaturized])
{
*ret = Minimized;
return S_OK;
}
- if([Window isZoomed])
+ if(IsZoomed())
{
*ret = Maximized;
return S_OK;
@@ -611,16 +766,57 @@ private:
}
}
+ void EnterFullScreenMode ()
+ {
+ _fullScreenActive = true;
+
+ [Window setHasShadow:YES];
+ [Window setTitleVisibility:NSWindowTitleVisible];
+ [Window setTitlebarAppearsTransparent:NO];
+ [Window setTitle:_lastTitle];
+
+ [Window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable];
+
+ [Window toggleFullScreen:nullptr];
+ }
+
+ void ExitFullScreenMode ()
+ {
+ [Window toggleFullScreen:nullptr];
+
+ _fullScreenActive = false;
+
+ SetDecorations(_decorations);
+ }
+
virtual HRESULT SetWindowState (AvnWindowState state) override
{
@autoreleasepool
{
+ if(_lastWindowState == state)
+ {
+ return S_OK;
+ }
+
+ _inSetWindowState = true;
+
+ auto currentState = _lastWindowState;
_lastWindowState = state;
+ if(currentState == Normal)
+ {
+ _preZoomSize = [Window frame];
+ }
+
if(_shown)
{
switch (state) {
case Maximized:
+ if(currentState == FullScreen)
+ {
+ ExitFullScreenMode();
+ }
+
lastPositionSet.X = 0;
lastPositionSet.Y = 0;
@@ -636,40 +832,66 @@ private:
break;
case Minimized:
- [Window miniaturize:Window];
+ if(currentState == FullScreen)
+ {
+ ExitFullScreenMode();
+ }
+ else
+ {
+ [Window miniaturize:Window];
+ }
break;
- default:
+ case FullScreen:
if([Window isMiniaturized])
{
[Window deminiaturize:Window];
}
+ EnterFullScreenMode();
+ break;
+
+ case Normal:
+ if([Window isMiniaturized])
+ {
+ [Window deminiaturize:Window];
+ }
+
+ if(currentState == FullScreen)
+ {
+ ExitFullScreenMode();
+ }
+
if(IsZoomed())
{
- DoZoom();
+ if(_decorations == SystemDecorationsFull)
+ {
+ DoZoom();
+ }
+ else
+ {
+ [Window setFrame:_preZoomSize display:true];
+ auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
+
+ [View setFrameSize:newFrame];
+ }
+
}
break;
}
}
+ _inSetWindowState = false;
+
return S_OK;
}
}
virtual void OnResized () override
{
- if(_shown)
+ if(_shown && !_inSetWindowState && !_transitioningWindowState)
{
- auto windowState = [Window isMiniaturized] ? Minimized
- : (IsZoomed() ? Maximized : Normal);
-
- if (windowState != _lastWindowState)
- {
- _lastWindowState = windowState;
-
- WindowEvents->WindowStateChanged(windowState);
- }
+ WindowStateChanged();
}
}
@@ -678,9 +900,10 @@ protected:
{
unsigned long s = NSWindowStyleMaskBorderless;
- switch (_hasDecorations)
+ switch (_decorations)
{
case SystemDecorationsNone:
+ s = s | NSWindowStyleMaskFullSizeContentView;
break;
case SystemDecorationsBorderOnly:
@@ -688,21 +911,35 @@ protected:
break;
case SystemDecorationsFull:
- s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskBorderless;
+ s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless;
+
if(_canResize)
{
s = s | NSWindowStyleMaskResizable;
}
-
break;
}
+ if([Window parentWindow] == nullptr)
+ {
+ s |= NSWindowStyleMaskMiniaturizable;
+ }
return s;
}
};
NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
+@implementation AutoFitContentVisualEffectView
+-(void)setFrameSize:(NSSize)newSize
+{
+ [super setFrameSize:newSize];
+ if([[self subviews] count] == 0)
+ return;
+ [[self subviews][0] setFrameSize: newSize];
+}
+@end
+
@implementation AvnView
{
ComPtr _parent;
@@ -752,7 +989,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_area = nullptr;
_lastPixelSize.Height = 100;
_lastPixelSize.Width = 100;
-
+ [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]];
return self;
}
@@ -878,15 +1115,28 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (bool) ignoreUserInput
{
auto parentWindow = objc_cast([self window]);
+
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents])
+ {
+ auto window = dynamic_cast(_parent.getRaw());
+
+ if(window != nullptr)
+ {
+ window->WindowEvents->GotInputWhenDisabled();
+ }
+
return TRUE;
+ }
+
return FALSE;
}
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
{
if([self ignoreUserInput])
+ {
return;
+ }
[self becomeFirstResponder];
auto localPoint = [self convertPoint:[event locationInWindow] toView:self];
@@ -1031,7 +1281,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type
{
if([self ignoreUserInput])
+ {
return;
+ }
+
auto key = s_KeyMap[[event keyCode]];
auto timestamp = [event timestamp] * 1000;
@@ -1143,6 +1396,68 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
return result;
}
+
+- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info
+{
+ auto localPoint = [self convertPoint:[info draggingLocation] toView:self];
+ auto avnPoint = [self toAvnPoint:localPoint];
+ auto point = [self translateLocalPoint:avnPoint];
+ auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]];
+ NSDragOperation nsop = [info draggingSourceOperationMask];
+
+ auto effects = ConvertDragDropEffects(nsop);
+ int reffects = (int)_parent->BaseEvents
+ ->DragEvent(type, point, modifiers, effects,
+ CreateClipboard([info draggingPasteboard], nil),
+ GetAvnDataObjectHandleFromDraggingInfo(info));
+
+ NSDragOperation ret = 0;
+
+ // Ensure that the managed part didn't add any new effects
+ reffects = (int)effects & (int)reffects;
+
+ // OSX requires exactly one operation
+ if((reffects & (int)AvnDragDropEffects::Copy) != 0)
+ ret = NSDragOperationCopy;
+ else if((reffects & (int)AvnDragDropEffects::Move) != 0)
+ ret = NSDragOperationMove;
+ else if((reffects & (int)AvnDragDropEffects::Link) != 0)
+ ret = NSDragOperationLink;
+ if(ret == 0)
+ ret = NSDragOperationNone;
+ return ret;
+}
+
+- (NSDragOperation)draggingEntered:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender];
+}
+
+- (NSDragOperation)draggingUpdated:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender];
+}
+
+- (void)draggingExited:(id )sender
+{
+ [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender];
+}
+
+- (BOOL)prepareForDragOperation:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone;
+}
+
+- (BOOL)performDragOperation:(id )sender
+{
+ return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone;
+}
+
+- (void)concludeDragOperation:(nullable id )sender
+{
+
+}
+
@end
@@ -1151,8 +1466,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
ComPtr _parent;
bool _canBecomeKeyAndMain;
bool _closed;
- NSMenu* _menu;
- bool _isAppMenuApplied;
+ bool _isEnabled;
+ AvnMenu* _menu;
double _lastScaling;
}
@@ -1172,6 +1487,20 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
}
+- (void)performClose:(id)sender
+{
+ if([[self delegate] respondsToSelector:@selector(windowShouldClose:)])
+ {
+ if(![[self delegate] windowShouldClose:self]) return;
+ }
+ else if([self respondsToSelector:@selector(windowShouldClose:)])
+ {
+ if(![self windowShouldClose:self]) return;
+ }
+
+ [self close];
+}
+
- (void)pollModalSession:(nonnull NSModalSession)session
{
auto response = [NSApp runModalSession:session];
@@ -1189,32 +1518,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);
+
+ [[appMenuItem menu] removeItem:appMenuItem];
- if(appMenu != nullptr)
+ 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;
@@ -1227,6 +1588,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent = parent;
[self setDelegate:self];
_closed = false;
+ _isEnabled = true;
_lastScaling = [self backingScaleFactor];
[self setOpaque:NO];
@@ -1293,14 +1655,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
-(bool)shouldTryToHandleEvents
{
- for(NSWindow* uch in [self childWindows])
- {
- auto ch = objc_cast(uch);
- if(ch == nil)
- continue;
- return FALSE;
- }
- return TRUE;
+ return _isEnabled;
+}
+
+-(void) setEnabled:(bool)enable
+{
+ _isEnabled = enable;
}
-(void)makeKeyWindow
@@ -1315,23 +1675,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];
@@ -1370,39 +1714,79 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
- (void)windowDidResize:(NSNotification *)notification
{
- _parent->OnResized();
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->WindowStateChanged();
+ }
}
-- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+- (void)windowWillExitFullScreen:(NSNotification *)notification
{
- return true;
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->StartStateTransition();
+ }
}
--(void)resignKeyWindow
+- (void)windowDidExitFullScreen:(NSNotification *)notification
{
- if(_parent)
- _parent->BaseEvents->Deactivated();
-
- auto appMenuItem = ::GetAppMenuItem();
+ auto parent = dynamic_cast(_parent.operator->());
- if(appMenuItem != nullptr)
+ if(parent != nullptr)
{
- auto appMenu = ::GetAppMenu();
+ parent->EndStateTransition();
- auto nativeAppMenu = dynamic_cast(appMenu);
-
- [[appMenuItem menu] removeItem:appMenuItem];
+ if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized)
+ {
+ NSRect screenRect = [[self screen] visibleFrame];
+ [self setFrame:screenRect display:YES];
+ }
- [nativeAppMenu->GetNative() addItem:appMenuItem];
+ if(parent->WindowState() == Minimized)
+ {
+ [self miniaturize:nullptr];
+ }
- [NSApp setMenu:nativeAppMenu->GetNative()];
+ parent->WindowStateChanged();
}
- else
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
{
- [NSApp setMenu:nullptr];
+ parent->StartStateTransition();
+ }
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification
+{
+ auto parent = dynamic_cast(_parent.operator->());
+
+ if(parent != nullptr)
+ {
+ parent->EndStateTransition();
+ parent->WindowStateChanged();
}
+}
+
+- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
+{
+ return true;
+}
+
+-(void)resignKeyWindow
+{
+ if(_parent)
+ _parent->BaseEvents->Deactivated();
- // remove window menu items from appmenu?
+ [self showAppMenuOnly];
[super resignKeyWindow];
}
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index 358846a14e..5460dc7720 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -12,6 +12,7 @@ using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.MSBuild;
+using Nuke.Common.Tools.Npm;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
using static Nuke.Common.EnvironmentInfo;
@@ -121,8 +122,21 @@ partial class Build : NukeBuild
EnsureCleanDirectory(Parameters.TestResultsRoot);
});
+ Target CompileHtmlPreviewer => _ => _
+ .DependsOn(Clean)
+ .Executes(() =>
+ {
+ var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
+
+ NpmTasks.NpmInstall(c => c.SetWorkingDirectory(webappDir));
+ NpmTasks.NpmRun(c => c
+ .SetWorkingDirectory(webappDir)
+ .SetCommand("dist"));
+ });
+
Target Compile => _ => _
.DependsOn(Clean)
+ .DependsOn(CompileHtmlPreviewer)
.Executes(() =>
{
if (Parameters.IsRunningOnWindows)
diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets
index 552713f94b..537495fcad 100644
--- a/packages/Avalonia/AvaloniaBuildTasks.targets
+++ b/packages/Avalonia/AvaloniaBuildTasks.targets
@@ -2,6 +2,7 @@
<_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild)
<_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false
+ low
+ EmbeddedResources="@(EmbeddedResources)"
+ ReportImportance="$(AvaloniaXamlReportImportance)"/>
@@ -67,6 +69,7 @@
OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
ProjectDirectory="$(MSBuildProjectDirectory)"
VerifyIl="$(AvaloniaXamlIlVerifyIl)"
+ ReportImportance="$(AvaloniaXamlReportImportance)"
/>
-
+
-
+
diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
index 22d01e0765..a66038ff3e 100644
--- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs
@@ -6,6 +6,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using System.Threading;
using ReactiveUI;
+using Avalonia.Controls;
namespace BindingDemo.ViewModels
{
@@ -27,7 +28,7 @@ namespace BindingDemo.ViewModels
Detail = "Item " + x + " details",
}));
- SelectedItems = new ObservableCollection();
+ Selection = new SelectionModel();
ShuffleItems = ReactiveCommand.Create(() =>
{
@@ -56,7 +57,7 @@ namespace BindingDemo.ViewModels
}
public ObservableCollection Items { get; }
- public ObservableCollection SelectedItems { get; }
+ public SelectionModel Selection { get; }
public ReactiveCommand ShuffleItems { get; }
public string BooleanString
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index 854cae484c..2a5294cacc 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
@@ -6,6 +7,7 @@ using System.Threading;
using Avalonia;
using Avalonia.ReactiveUI;
using Avalonia.Dialogs;
+using Avalonia.OpenGL;
namespace ControlCatalog.NetCore
{
diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml
index e40509dfda..c7a75f5a70 100644
--- a/samples/ControlCatalog/App.xaml
+++ b/samples/ControlCatalog/App.xaml
@@ -1,9 +1,7 @@
-
-
-
+
-
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ButtonPage.xaml.cs b/samples/ControlCatalog/Pages/ButtonPage.xaml.cs
index 1d0c228a0e..5e555c8c91 100644
--- a/samples/ControlCatalog/Pages/ButtonPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ButtonPage.xaml.cs
@@ -5,5 +5,25 @@ namespace ControlCatalog.Pages
{
public class ButtonPage : UserControl
{
+ private int repeatButtonClickCount = 0;
+
+ public ButtonPage()
+ {
+ InitializeComponent();
+
+ this.FindControl("RepeatButton").Click += OnRepeatButtonClick;
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public void OnRepeatButtonClick(object sender, object args)
+ {
+ repeatButtonClickCount++;
+ var textBlock = this.FindControl("RepeatButtonTextBlock");
+ textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
+ }
}
}
diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
index bbfbd4b4cd..486cc55d44 100644
--- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml
@@ -6,7 +6,7 @@
A drop-down list.
-
+
Inline Items
Inline Item 2
Inline Item 3
diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml
index 1ed5c0cbfd..0834e829d8 100644
--- a/samples/ControlCatalog/Pages/DialogsPage.xaml
+++ b/samples/ControlCatalog/Pages/DialogsPage.xaml
@@ -6,6 +6,7 @@
+
diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
index ba1921a185..dcb94a89e7 100644
--- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
+using Avalonia.Dialogs;
using Avalonia.Markup.Xaml;
#pragma warning disable 4014
@@ -58,6 +59,19 @@ namespace ControlCatalog.Pages
Title = "Select folder",
}.ShowAsync(GetWindow());
};
+ this.FindControl