diff --git a/.gitignore b/.gitignore
index bbf358b8f4..ee778ed4e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,6 +102,7 @@ csx
AppPackages/
# NCrunch
+.NCrunch_*/
_NCrunch_*/
*.ncrunchsolution.user
nCrunchTemp_*
diff --git a/.ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject b/.ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Avalonia.Generators.Tests.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.ncrunch/Generators.Sandbox.v3.ncrunchproject b/.ncrunch/Generators.Sandbox.v3.ncrunchproject
new file mode 100644
index 0000000000..319cd523ce
--- /dev/null
+++ b/.ncrunch/Generators.Sandbox.v3.ncrunchproject
@@ -0,0 +1,5 @@
+
+
+ True
+
+
\ No newline at end of file
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
index 5bbc3d6915..875161d336 100644
--- a/.nuke/build.schema.json
+++ b/.nuke/build.schema.json
@@ -84,11 +84,11 @@
"GenerateCppHeaders",
"Package",
"RunCoreLibsTests",
- "RunDesignerTests",
"RunHtmlPreviewerTests",
"RunLeakTests",
"RunRenderTests",
"RunTests",
+ "RunToolsTests",
"ZipFiles"
]
}
@@ -123,11 +123,11 @@
"GenerateCppHeaders",
"Package",
"RunCoreLibsTests",
- "RunDesignerTests",
"RunHtmlPreviewerTests",
"RunLeakTests",
"RunRenderTests",
"RunTests",
+ "RunToolsTests",
"ZipFiles"
]
}
diff --git a/Avalonia.Desktop.slnf b/Avalonia.Desktop.slnf
index 4a7a329fc6..76620e8b93 100644
--- a/Avalonia.Desktop.slnf
+++ b/Avalonia.Desktop.slnf
@@ -8,9 +8,9 @@
"samples\\GpuInterop\\GpuInterop.csproj",
"samples\\IntegrationTestApp\\IntegrationTestApp.csproj",
"samples\\MiniMvvm\\MiniMvvm.csproj",
+ "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"samples\\SampleControls\\ControlSamples.csproj",
"samples\\Sandbox\\Sandbox.csproj",
- "samples\\ReactiveUIDemo\\ReactiveUIDemo.csproj",
"src\\Avalonia.Base\\Avalonia.Base.csproj",
"src\\Avalonia.Build.Tasks\\Avalonia.Build.Tasks.csproj",
"src\\Avalonia.Controls.ColorPicker\\Avalonia.Controls.ColorPicker.csproj",
@@ -21,6 +21,7 @@
"src\\Avalonia.Desktop\\Avalonia.Desktop.csproj",
"src\\Avalonia.Diagnostics\\Avalonia.Diagnostics.csproj",
"src\\Avalonia.Dialogs\\Avalonia.Dialogs.csproj",
+ "src\\Avalonia.Fonts.Inter\\Avalonia.Fonts.Inter.csproj",
"src\\Avalonia.FreeDesktop\\Avalonia.FreeDesktop.csproj",
"src\\Avalonia.Headless.Vnc\\Avalonia.Headless.Vnc.csproj",
"src\\Avalonia.Headless\\Avalonia.Headless.csproj",
@@ -37,12 +38,14 @@
"src\\Markup\\Avalonia.Markup.Xaml\\Avalonia.Markup.Xaml.csproj",
"src\\Markup\\Avalonia.Markup\\Avalonia.Markup.csproj",
"src\\Skia\\Avalonia.Skia\\Avalonia.Skia.csproj",
- "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
- "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
- "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
+ "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
+ "src\\tools\\Avalonia.Generators\\Avalonia.Generators.csproj",
"src\\tools\\DevAnalyzers\\DevAnalyzers.csproj",
"src\\tools\\DevGenerators\\DevGenerators.csproj",
"src\\tools\\PublicAnalyzers\\Avalonia.Analyzers.csproj",
+ "src\\Windows\\Avalonia.Direct2D1\\Avalonia.Direct2D1.csproj",
+ "src\\Windows\\Avalonia.Win32.Interop\\Avalonia.Win32.Interop.csproj",
+ "src\\Windows\\Avalonia.Win32\\Avalonia.Win32.csproj",
"tests\\Avalonia.Base.UnitTests\\Avalonia.Base.UnitTests.csproj",
"tests\\Avalonia.Benchmarks\\Avalonia.Benchmarks.csproj",
"tests\\Avalonia.Controls.DataGrid.UnitTests\\Avalonia.Controls.DataGrid.UnitTests.csproj",
@@ -62,4 +65,4 @@
"tests\\Avalonia.UnitTests\\Avalonia.UnitTests.csproj"
]
}
-}
+}
\ No newline at end of file
diff --git a/Avalonia.sln b/Avalonia.sln
index 56847bae31..b21df07628 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -244,6 +244,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ItemsRepeater.UnitTests", "tests\Avalonia.Controls.ItemsRepeater.UnitTests\Avalonia.Controls.ItemsRepeater.UnitTests.csproj", "{F4E36AA8-814E-4704-BC07-291F70F45193}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators", "src\tools\Avalonia.Generators\Avalonia.Generators.csproj", "{DDA28789-C21A-4654-86CE-D01E81F095C5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Fonts.Inter", "src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj", "{13F1135D-BA1A-435C-9C5B-A368D1D63DE4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generators.Sandbox", "samples\Generators.Sandbox\Generators.Sandbox.csproj", "{A82AD1BC-EBE6-4FC3-A13B-D52A50297533}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -555,9 +563,14 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75C47156-C5D8-44BC-A5A7-E8657C2248D6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C810060E-3809-4B74-A125-F11533AF9C1B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.ActiveCfg = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.Build.0 = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.Build.0 = Release|Any CPU
{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE0F0DD4-A70D-472B-BD5D-B7D32D0E9386}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -566,10 +579,22 @@ Global
{F4E36AA8-814E-4704-BC07-291F70F45193}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4E36AA8-814E-4704-BC07-291F70F45193}.Release|Any CPU.Build.0 = Release|Any CPU
- {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.ActiveCfg = Release|Any CPU
- {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Debug|Any CPU.Build.0 = Release|Any CPU
- {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C692FE73-43DB-49CE-87FC-F03ED61F25C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DDA28789-C21A-4654-86CE-D01E81F095C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DDA28789-C21A-4654-86CE-D01E81F095C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {13F1135D-BA1A-435C-9C5B-A368D1D63DE4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A82AD1BC-EBE6-4FC3-A13B-D52A50297533}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -635,8 +660,11 @@ Global
{90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
- {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{C692FE73-43DB-49CE-87FC-F03ED61F25C9} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
+ {DDA28789-C21A-4654-86CE-D01E81F095C5} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637}
+ {2D7C812B-7E73-4252-8EFD-BC8A4D5CCB9F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {F4E36AA8-814E-4704-BC07-291F70F45193} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
+ {A82AD1BC-EBE6-4FC3-A13B-D52A50297533} = {9B9E3891-2366-4253-A952-D08BCEB71098}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/azure-pipelines-integrationtests.yml b/azure-pipelines-integrationtests.yml
index 5735da19ab..939d09c959 100644
--- a/azure-pipelines-integrationtests.yml
+++ b/azure-pipelines-integrationtests.yml
@@ -1,11 +1,3 @@
-# Starter pipeline
-# Start with a minimal pipeline that you can customize to build and deploy your code.
-# Add steps that build, run tests, deploy, and more:
-# https://aka.ms/yaml
-
-trigger:
-- master
-
jobs:
- job: Mac
pool:
@@ -23,27 +15,41 @@ jobs:
version: 7.0.101
- script: system_profiler SPDisplaysDataType |grep Resolution
+ displayName: 'Get Resolution'
- script: |
+ arch="x64"
+ if [[ $(uname -m) == 'arm64' ]]; then
+ arch="arm64"
+ fi
+ sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
pkill node
- appium &
+ pkill testmanagerd
+ appium > appium.out &
pkill IntegrationTestApp
./build.sh CompileNative
rm -rf $(osascript -e "POSIX path of (path to application id \"net.avaloniaui.avalonia.integrationtestapp\")")
pkill IntegrationTestApp
./samples/IntegrationTestApp/bundle.sh
- open -n ./samples/IntegrationTestApp/bin/Debug/net7.0/osx-arm64/publish/IntegrationTestApp.app
+ open -n ./samples/IntegrationTestApp/bin/Debug/net7.0/osx-$arch/publish/IntegrationTestApp.app
pkill IntegrationTestApp
+ displayName: 'Build IntegrationTestApp'
- task: DotNetCoreCLI@2
+ displayName: 'Run Integration Tests'
inputs:
command: 'test'
projects: 'tests/Avalonia.IntegrationTests.Appium/Avalonia.IntegrationTests.Appium.csproj'
+ arguments: '-l "console;verbosity=detailed"'
- script: |
pkill IntegrationTestApp
pkill node
+ displayName: 'Stop Appium'
+ - publish: appium.out
+ displayName: 'Publish appium logs on failure'
+ condition: failed()
- job: Windows
pool:
@@ -67,11 +73,13 @@ jobs:
displayName: 'Start WinAppDriver'
- task: DotNetCoreCLI@2
+ displayName: 'Build IntegrationTestApp'
inputs:
command: 'build'
projects: 'samples/IntegrationTestApp/IntegrationTestApp.csproj'
- task: DotNetCoreCLI@2
+ displayName: 'Run Integration Tests'
retryCountOnTaskFailure: 3
inputs:
command: 'test'
diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props
index 4929578b60..a66bff4999 100644
--- a/build/SourceGenerators.props
+++ b/build/SourceGenerators.props
@@ -1,5 +1,10 @@
-
+
+ true
+ false
+
+
+
+
+
+
+
+
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 c49290314d..eb0b528fd3 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
@@ -44,6 +44,9 @@
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; };
+ 8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */; };
+ 8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */ = {isa = PBXBuildFile; fileRef = 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */; };
+ 8D300D69292E1E5D00320C49 /* AvnTextInputMethod.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
@@ -97,6 +100,9 @@
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; };
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = ""; };
+ 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethodDelegate.h; sourceTree = ""; };
+ 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnTextInputMethod.h; sourceTree = ""; };
+ 8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnTextInputMethod.mm; sourceTree = ""; };
AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; };
AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -143,6 +149,9 @@
isa = PBXGroup;
children = (
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
+ 8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */,
+ 8D300D68292E1E5D00320C49 /* AvnTextInputMethod.mm */,
+ 8D300D64292D0A6800320C49 /* AvnTextInputMethod.h */,
BC11A5BC2608D58F0017BAD0 /* automation.h */,
BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
@@ -213,6 +222,8 @@
1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */,
18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */,
+ 8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */,
+ 8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */,
18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */,
18391ED5F611FF62C45F196D /* AvnView.h in Headers */,
18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */,
@@ -293,6 +304,7 @@
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
+ 8D300D69292E1E5D00320C49 /* AvnTextInputMethod.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
1AFD334123E03C4F0042899B /* controlhost.mm in Sources */,
1A465D10246AB61600C5858B /* dnd.mm in Sources */,
diff --git a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h
new file mode 100644
index 0000000000..4e5116ee71
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.h
@@ -0,0 +1,46 @@
+//
+// AvnTextInputMethod.h
+// Avalonia.Native.OSX
+//
+// Created by Benedikt Stebner on 22.11.22.
+// Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AvnTextInputMethod_h
+#define AvnTextInputMethod_h
+
+#import
+
+#include "com.h"
+#include "comimpl.h"
+#include "avalonia-native.h"
+#import "AvnTextInputMethodDelegate.h"
+
+class AvnTextInputMethod: public virtual ComObject, public virtual IAvnTextInputMethod{
+private:
+ id _inputMethodDelegate;
+public:
+ FORWARD_IUNKNOWN()
+
+ BEGIN_INTERFACE_MAP()
+ INTERFACE_MAP_ENTRY(IAvnTextInputMethod, IID_IAvnTextInputMethod)
+ END_INTERFACE_MAP()
+
+ virtual ~AvnTextInputMethod();
+
+ AvnTextInputMethod(id inputMethodDelegate);
+
+ bool IsActive ();
+
+ HRESULT SetClient (IAvnTextInputMethodClient* client) override;
+
+ virtual void Reset () override;
+
+ virtual void SetCursorRect (AvnRect rect) override;
+
+ virtual void SetSurroundingText (char* text, int anchorOffset, int cursorOffset) override;
+
+public:
+ ComPtr Client;
+};
+#endif /* AvnTextInputMethod_h */
diff --git a/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm
new file mode 100644
index 0000000000..8c3ae080fa
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnTextInputMethod.mm
@@ -0,0 +1,41 @@
+//
+// AvnTextInputMethod.mm
+// Avalonia.Native.OSX
+//
+// Created by Benedikt Stebner on 23.11.22.
+// Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#include "AvnTextInputMethod.h"
+
+AvnTextInputMethod::~AvnTextInputMethod() {
+ Client = nullptr;
+}
+
+AvnTextInputMethod::AvnTextInputMethod(id inputMethodDelegate) {
+ _inputMethodDelegate = inputMethodDelegate;
+}
+
+bool AvnTextInputMethod::IsActive() {
+ return Client != nullptr;
+}
+
+HRESULT AvnTextInputMethod::SetClient(IAvnTextInputMethodClient *client) {
+ START_COM_CALL;
+
+ Client = client;
+
+ return S_OK;
+}
+
+void AvnTextInputMethod::Reset() {
+}
+
+void AvnTextInputMethod::SetSurroundingText(char* text, int anchorOffset, int cursorOffset) {
+ [_inputMethodDelegate setText:[NSString stringWithUTF8String:text]];
+ [_inputMethodDelegate setSelection: anchorOffset : cursorOffset];
+}
+
+void AvnTextInputMethod::SetCursorRect(AvnRect rect) {
+ [_inputMethodDelegate setCursorRect: rect];
+}
diff --git a/native/Avalonia.Native/src/OSX/AvnTextInputMethodDelegate.h b/native/Avalonia.Native/src/OSX/AvnTextInputMethodDelegate.h
new file mode 100644
index 0000000000..9f321ca595
--- /dev/null
+++ b/native/Avalonia.Native/src/OSX/AvnTextInputMethodDelegate.h
@@ -0,0 +1,20 @@
+//
+// AvnTextInputMethodHost.h
+// Avalonia.Native.OSX
+//
+// Created by Benedikt Stebner on 24.11.22.
+// Copyright © 2022 Avalonia. All rights reserved.
+//
+
+#ifndef AvnTextInputMethodHost_h
+#define AvnTextInputMethodHost_h
+
+@protocol AvnTextInputMethodDelegate
+@required
+-(void) setText:(NSString* _Nonnull) text;
+-(void) setCursorRect:(AvnRect) cursorRect;
+-(void) setSelection: (int) start : (int) end;
+
+@end
+
+#endif /* AvnTextInputMethodHost_h */
diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h
index 86a68d34c5..256caa70e9 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.h
+++ b/native/Avalonia.Native/src/OSX/AvnView.h
@@ -5,8 +5,6 @@
#pragma once
#import
-
-#import
#import
#include "common.h"
#include "WindowImpl.h"
@@ -14,7 +12,7 @@
@class AvnAccessibilityElement;
-@interface AvnView : NSView
+@interface AvnView : NSView
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent;
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
@@ -24,4 +22,4 @@
-(AvnPlatformResizeReason) getResizeReason;
-(void) setResizeReason:(AvnPlatformResizeReason)reason;
+ (AvnPoint)toAvnPoint:(CGPoint)p;
-@end
\ No newline at end of file
+@end
diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm
index 4ae6ad5a00..fdc144e3a5 100644
--- a/native/Avalonia.Native/src/OSX/AvnView.mm
+++ b/native/Avalonia.Native/src/OSX/AvnView.mm
@@ -12,6 +12,7 @@
{
ComPtr _parent;
NSTrackingArea* _area;
+ NSMutableAttributedString* _markedText;
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed;
AvnInputModifiers _modifierState;
NSEvent* _lastMouseDownEvent;
@@ -20,6 +21,9 @@
NSObject* _renderTarget;
AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild;
+ NSRect _cursorRect;
+ NSMutableString* _text;
+ NSRange _selection;
}
- (void)onClosed
@@ -127,11 +131,8 @@
[self updateRenderTarget];
auto reason = [self inLiveResize] ? ResizeUser : _resizeReason;
-
- if(_parent->IsShown())
- {
- _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
- }
+
+ _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason);
}
}
@@ -521,7 +522,7 @@
- (void)keyDown:(NSEvent *)event
{
[self keyboardEvent:event withType:KeyDown];
- [[self inputContext] handleEvent:event];
+ _lastKeyHandled = [[self inputContext] handleEvent:event];
[super keyDown:event];
}
@@ -560,27 +561,50 @@
- (BOOL)hasMarkedText
{
- return _lastKeyHandled;
+ return [_markedText length] > 0;
}
- (NSRange)markedRange
{
+ if([_markedText length] > 0)
+ return NSMakeRange(0, [_markedText length] - 1);
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)selectedRange
{
- return NSMakeRange(NSNotFound, 0);
+ return _selection;
}
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
-
+ if([string isKindOfClass:[NSAttributedString class]])
+ {
+ _markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string];
+ }
+ else
+ {
+ _markedText = [[NSMutableAttributedString alloc] initWithString:string];
+ }
+
+ if(!_parent->InputMethod->IsActive()){
+ return;
+ }
+
+ _parent->InputMethod->Client->SetPreeditText((char*)[_markedText.string UTF8String]);
}
- (void)unmarkText
{
-
+ [[_markedText mutableString] setString:@""];
+
+ [[self inputContext] discardMarkedText];
+
+ if(!_parent->InputMethod->IsActive()){
+ return;
+ }
+
+ _parent->InputMethod->Client->SetPreeditText(nullptr);
}
- (NSArray *)validAttributesForMarkedText
@@ -590,30 +614,33 @@
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- return [NSAttributedString new];
+ return nullptr;
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- if(!_lastKeyHandled)
+ [self unmarkText];
+
+ if(_parent != nullptr)
{
- if(_parent != nullptr)
- {
- _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
- }
+ _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]);
}
+
+ [[self inputContext] invalidateCharacterCoordinates];
}
- (NSUInteger)characterIndexForPoint:(NSPoint)point
{
- return 0;
+ return NSNotFound;
}
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- CGRect result = { 0 };
-
- return result;
+ if(!_parent->InputMethod->IsActive()){
+ return NSZeroRect;
+ }
+
+ return _cursorRect;
}
- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info
@@ -718,4 +745,28 @@
return [[self accessibilityChild] accessibilityFocusedUIElement];
}
+- (void) setText:(NSString *)text{
+ [_text setString:text];
+
+ [[self inputContext] discardMarkedText];
+}
+
+- (void) setSelection:(int)start :(int)end{
+ _selection = NSMakeRange(start, end - start);
+
+ [[self inputContext] invalidateCharacterCoordinates];
+}
+
+- (void) setCursorRect:(AvnRect)rect{
+ NSRect cursorRect = ToNSRect(rect);
+ NSRect windowRectOnScreen = [[self window] convertRectToScreen:self.frame];
+
+ windowRectOnScreen.size = cursorRect.size;
+ windowRectOnScreen.origin = NSMakePoint(windowRectOnScreen.origin.x + cursorRect.origin.x, windowRectOnScreen.origin.y + self.frame.size.height - cursorRect.origin.y - cursorRect.size.height);
+
+ _cursorRect = windowRectOnScreen;
+
+ [[self inputContext] invalidateCharacterCoordinates];
+}
+
@end
diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm
index b1fb915e04..16e1486acc 100644
--- a/native/Avalonia.Native/src/OSX/AvnWindow.mm
+++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm
@@ -394,7 +394,7 @@
- (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame
{
- return true;
+ return _parent->CanZoom();
}
-(void)windowDidResignKey:(NSNotification *)notification
diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index 83ab1bfd01..85f4b7c50a 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -41,7 +41,7 @@ public:
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = ConvertPointY(ToAvnPoint([screen visibleFrame].origin)).Y - ret->WorkingArea.Height;
- ret->Scaling = [screen backingScaleFactor];
+ ret->Scaling = 1;
ret->IsPrimary = index == 0;
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
index 93decef136..bc35ca670f 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h
@@ -8,6 +8,7 @@
#include "rendertarget.h"
#include "INSWindowHolder.h"
+#include "AvnTextInputMethod.h"
@class AutoFitContentView;
@class AvnMenu;
@@ -103,7 +104,11 @@ BEGIN_INTERFACE_MAP()
id GetWindowProtocol ();
virtual void BringToFront ();
+
+ virtual HRESULT GetInputMethod(IAvnTextInputMethod **retOut) override;
+ virtual bool CanZoom() { return false; }
+
protected:
virtual NSWindowStyleMask CalculateStyleMask() = 0;
virtual void UpdateStyle();
@@ -130,6 +135,7 @@ public:
NSObject *renderTarget;
NSWindow * Window;
ComPtr BaseEvents;
+ ComPtr InputMethod;
AvnView *View;
};
diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
index 59102e15a6..edf6bcf508 100644
--- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm
@@ -4,6 +4,7 @@
//
#import
+#import
#include "common.h"
#include "AvnView.h"
#include "menu.h"
@@ -14,6 +15,7 @@
#import "WindowProtocol.h"
#import "WindowInterfaces.h"
#include "WindowBaseImpl.h"
+#include "AvnTextInputMethod.h"
WindowBaseImpl::~WindowBaseImpl() {
@@ -28,6 +30,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl,
_glContext = gl;
renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl];
View = [[AvnView alloc] initWithParent:this];
+ InputMethod = new AvnTextInputMethod(View);
StandardContainer = [[AutoFitContentView new] initWithContent:View];
lastPositionSet = { 0, 0 };
@@ -293,15 +296,24 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso
}
@try {
- if(x != lastSize.width || y != lastSize.height) {
- lastSize = NSSize{x, y};
-
+ if(x != lastSize.width || y != lastSize.height)
+ {
if (!_shown) {
- BaseEvents->Resized(AvnSize{x, y}, reason);
- } else if (Window != nullptr) {
- [Window setContentSize:lastSize];
- [Window invalidateShadow];
+ auto screenSize = [Window screen].visibleFrame.size;
+
+ if (x > screenSize.width) {
+ x = screenSize.width;
+ }
+
+ if (y > screenSize.height) {
+ y = screenSize.height;
+ }
}
+
+ lastSize = NSSize{x, y};
+
+ [Window setContentSize:lastSize];
+ [Window invalidateShadow];
}
}
@finally {
@@ -595,6 +607,14 @@ void WindowBaseImpl::BringToFront()
// do nothing.
}
+HRESULT WindowBaseImpl::GetInputMethod(IAvnTextInputMethod **retOut) {
+ START_COM_CALL;
+
+ *retOut = InputMethod;
+
+ return S_OK;
+}
+
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
{
@autoreleasepool
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h
index 29bb659039..5140124a17 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.h
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.h
@@ -97,6 +97,8 @@ BEGIN_INTERFACE_MAP()
bool CanBecomeKeyWindow ();
+ bool CanZoom() override { return _isEnabled && _canResize; }
+
protected:
virtual NSWindowStyleMask CalculateStyleMask() override;
void UpdateStyle () override;
diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm
index cf1ee6943d..925880534a 100644
--- a/native/Avalonia.Native/src/OSX/WindowImpl.mm
+++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm
@@ -54,6 +54,11 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) {
WindowBaseImpl::Show(activate, isDialog);
GetWindowState(&_actualWindowState);
+
+ if(IsZoomed()) {
+ _lastWindowState = _actualWindowState;
+ }
+
return SetWindowState(_lastWindowState);
}
}
@@ -276,10 +281,13 @@ HRESULT WindowImpl::SetDecorations(SystemDecorations value) {
case SystemDecorationsFull:
[Window setHasShadow:YES];
- [Window setTitleVisibility:NSWindowTitleVisible];
- [Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle];
+ if (!_isClientAreaExtended) {
+ [Window setTitleVisibility:NSWindowTitleVisible];
+ [Window setTitlebarAppearsTransparent:NO];
+ }
+
if (currentWindowState == Maximized) {
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size;
@@ -606,7 +614,8 @@ void WindowImpl::UpdateStyle() {
}
bool wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
- bool hasTrafficLights = _isClientAreaExtended ? wantsChrome : _decorations == SystemDecorationsFull;
+ bool hasTrafficLights = (_decorations == SystemDecorationsFull) &&
+ (_isClientAreaExtended ? wantsChrome : true);
NSButton* closeButton = [Window standardWindowButton:NSWindowCloseButton];
NSButton* miniaturizeButton = [Window standardWindowButton:NSWindowMiniaturizeButton];
@@ -617,5 +626,5 @@ void WindowImpl::UpdateStyle() {
[miniaturizeButton setHidden:!hasTrafficLights];
[miniaturizeButton setEnabled:_isEnabled];
[zoomButton setHidden:!hasTrafficLights];
- [zoomButton setEnabled:_isEnabled && _canResize];
+ [zoomButton setEnabled:CanZoom()];
}
diff --git a/native/Avalonia.Native/src/OSX/platformthreading.mm b/native/Avalonia.Native/src/OSX/platformthreading.mm
index 6d5bd4aa02..d80df68fea 100644
--- a/native/Avalonia.Native/src/OSX/platformthreading.mm
+++ b/native/Avalonia.Native/src/OSX/platformthreading.mm
@@ -1,193 +1,266 @@
#include "common.h"
class PlatformThreadingInterface;
+
+
+class LoopCancellation : public ComSingleObject
+{
+public:
+ FORWARD_IUNKNOWN()
+
+ bool Running = false;
+ bool Cancelled = false;
+ bool IsApp = false;
+
+ virtual void Cancel() override
+ {
+ Cancelled = true;
+ if(Running)
+ {
+ Running = false;
+ if(![NSThread isMainThread])
+ {
+ AddRef();
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if(Release() == 0)
+ return;
+ Cancel();
+ });
+ return;
+ };
+ if(IsApp)
+ [NSApp stop:nil];
+ else
+ {
+ // Wakeup the event loop
+ NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
+ location:NSMakePoint(0, 0)
+ modifierFlags:0
+ timestamp:0
+ windowNumber:0
+ context:nil
+ subtype:0
+ data1:0
+ data2:0];
+ [NSApp postEvent:event atStart:YES];
+ }
+ }
+ };
+};
+
+// CFRunLoopTimerSetNextFireDate docs recommend to "create a repeating timer with an initial
+// firing time in the distant future (or the initial firing time) and a very large repeat
+// interval—on the order of decades or more"
+static double distantFutureInterval = (double)50*365*24*3600;
+
@interface Signaler : NSObject
--(void) setParent: (PlatformThreadingInterface*)parent;
--(void) signal: (int) priority;
+-(void) setEvents:(IAvnPlatformThreadingInterfaceEvents*) events;
+-(void) updateTimer:(int)ms;
-(Signaler*) init;
+-(void) destroyObserver;
+-(void) signal;
@end
-@implementation ActionCallback
+@implementation Signaler
{
- ComPtr _callback;
+ ComPtr _events;
+ bool _wakeupDelegateSent;
+ bool _signaled;
+ bool _backgroundProcessingRequested;
+ CFRunLoopObserverRef _observer;
+ CFRunLoopTimerRef _timer;
+}
+- (void) checkSignaled
+{
+ bool signaled;
+ @synchronized (self) {
+ signaled = _signaled;
+ _signaled = false;
+ }
+ if(signaled)
+ {
+ _events->Signaled();
+ }
}
-- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback
+
+- (Signaler*) init
{
- _callback = callback;
+ _observer = CFRunLoopObserverCreateWithHandler(nil,
+ kCFRunLoopBeforeSources
+ | kCFRunLoopAfterWaiting
+ | kCFRunLoopBeforeWaiting
+ ,
+ true, 0,
+ ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
+ if(activity == kCFRunLoopBeforeWaiting)
+ {
+ bool triggerProcessing;
+ @synchronized (self) {
+ triggerProcessing = self->_backgroundProcessingRequested;
+ self->_backgroundProcessingRequested = false;
+ }
+ if(triggerProcessing)
+ self->_events->ReadyForBackgroundProcessing();
+ }
+ [self checkSignaled];
+ });
+ CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);
+
+
+ _timer = CFRunLoopTimerCreateWithHandler(nil, CFAbsoluteTimeGetCurrent() + distantFutureInterval, distantFutureInterval, 0, 0, ^(CFRunLoopTimerRef timer) {
+ self->_events->Timer();
+ });
+
+ CFRunLoopAddTimer(CFRunLoopGetMain(), _timer, kCFRunLoopCommonModes);
+
return self;
}
-- (void) action
+- (void) destroyObserver
{
- _callback->Run();
+ if(_observer != nil)
+ {
+ CFRunLoopObserverInvalidate(_observer);
+ CFRelease(_observer);
+ _observer = nil;
+ }
+
+ if(_timer != nil)
+ {
+ CFRunLoopTimerInvalidate(_timer);
+ CFRelease(_timer);
+ _timer = nil;
+ }
}
+-(void) updateTimer:(int)ms
+{
+ if(_timer == nil)
+ return;
+ double interval = ms < 0 ? distantFutureInterval : ((double)ms / 1000);
+ CFRunLoopTimerSetTolerance(_timer, 0);
+ CFRunLoopTimerSetNextFireDate(_timer, CFAbsoluteTimeGetCurrent() + interval);
+}
-@end
+- (void) setEvents: (IAvnPlatformThreadingInterfaceEvents*) events
+{
+ _events = events;
+}
-class TimerWrapper : public ComUnknownObject
+- (void) signal
{
- NSTimer* _timer;
-public:
- TimerWrapper(IAvnActionCallback* callback, int ms)
- {
- auto cb = [[ActionCallback alloc] initWithCallback:callback];
- _timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(double)ms/1000 target:cb selector:@selector(action) userInfo:nullptr repeats:true];
+ @synchronized (self) {
+ if(_signaled)
+ return;
+ _signaled = true;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self checkSignaled];
+ });
+ CFRunLoopWakeUp(CFRunLoopGetMain());
}
-
- virtual ~TimerWrapper()
- {
- [_timer invalidate];
+}
+
+- (void) requestBackgroundProcessing
+{
+ @synchronized (self) {
+ if(_backgroundProcessingRequested)
+ return;
+ _backgroundProcessingRequested = true;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ // This is needed to wakeup the loop if we are called from inside of BeforeWait hook
+ });
}
-};
+
+
+}
+@end
class PlatformThreadingInterface : public ComSingleObject
{
private:
+ ComPtr _events;
Signaler* _signaler;
- bool _wasRunningAtLeastOnce = false;
-
- class LoopCancellation : public ComSingleObject
- {
- public:
- FORWARD_IUNKNOWN()
-
- bool Running = false;
- bool Cancelled = false;
-
- virtual void Cancel() override
- {
- Cancelled = true;
- if(Running)
- {
- Running = false;
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSApplication sharedApplication] stop:nil];
- NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined
- location:NSMakePoint(0, 0)
- modifierFlags:0
- timestamp:0
- windowNumber:0
- context:nil
- subtype:0
- data1:0
- data2:0];
- [NSApp postEvent:event atStart:YES];
- });
- }
- }
-
- };
-
+ CFRunLoopObserverRef _observer = nil;
public:
FORWARD_IUNKNOWN()
- ComPtr SignaledCallback;
-
PlatformThreadingInterface()
{
_signaler = [Signaler new];
- [_signaler setParent:this];
- }
+ };
~PlatformThreadingInterface()
{
- if(_signaler)
- [_signaler setParent: NULL];
- _signaler = NULL;
+ [_signaler destroyObserver];
}
- virtual bool GetCurrentThreadIsLoopThread() override
+ bool GetCurrentThreadIsLoopThread() override
{
return [NSThread isMainThread];
- }
- virtual void SetSignaledCallback(IAvnSignaledCallback* cb) override
+ };
+
+
+
+ void SetEvents(IAvnPlatformThreadingInterfaceEvents *cb) override
{
- SignaledCallback = cb;
- }
- virtual IAvnLoopCancellation* CreateLoopCancellation() override
+ _events = cb;
+ [_signaler setEvents:cb];
+ };
+
+ IAvnLoopCancellation *CreateLoopCancellation() override
{
return new LoopCancellation();
- }
+ };
- virtual HRESULT RunLoop(IAvnLoopCancellation* cancel) override
+ void RunLoop(IAvnLoopCancellation *cancel) override
{
START_COM_CALL;
-
auto can = dynamic_cast(cancel);
if(can->Cancelled)
- return S_OK;
- if(_wasRunningAtLeastOnce)
- return E_FAIL;
+ return;
can->Running = true;
- _wasRunningAtLeastOnce = true;
- [NSApp run];
- return S_OK;
- }
+ if(![NSApp isRunning])
+ {
+ can->IsApp = true;
+ [NSApp run];
+ return;
+ }
+ else
+ {
+ while(!can->Cancelled)
+ {
+ @autoreleasepool
+ {
+ NSEvent* ev = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate: [NSDate dateWithTimeIntervalSinceNow:1]
+ inMode:NSDefaultRunLoopMode
+ dequeue:true];
+ if(ev != NULL)
+ [NSApp sendEvent:ev];
+ }
+ }
+ }
+ };
- virtual void Signal(int priority) override
+ void Signal() override
{
- [_signaler signal:priority];
- }
+ [_signaler signal];
+ };
- virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) override
- {
- @autoreleasepool {
-
- return new TimerWrapper(callback, ms);
- }
- }
-};
-
-@implementation Signaler
-
-PlatformThreadingInterface* _parent = 0;
-bool _signaled = 0;
-NSArray* _modes;
-
--(Signaler*) init
-{
- if(self = [super init])
+ void UpdateTimer(int ms) override
{
- _modes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
- }
- return self;
-}
-
--(void) perform
-{
- ComPtr cb;
- @synchronized (self) {
- _signaled = false;
- if(_parent != NULL)
- cb = _parent->SignaledCallback;
- }
- if(cb != nullptr)
- cb->Signaled(0, false);
-}
-
--(void) setParent:(PlatformThreadingInterface *)parent
-{
- @synchronized (self) {
- _parent = parent;
- }
-}
-
--(void) signal: (int) priority
-{
-
- @synchronized (self) {
- if(_signaled)
- return;
- _signaled = true;
- [self performSelector:@selector(perform) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes:_modes];
+ [_signaler updateTimer:ms];
+ };
+
+ void RequestBackgroundProcessing() override {
+ [_signaler requestBackgroundProcessing];
}
-}
-@end
-
+
+};
extern IAvnPlatformThreadingInterface* CreatePlatformThreading()
{
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index 3704cee890..40232947d9 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -165,10 +165,10 @@ partial class Build : NukeBuild
foreach (var fw in targetFrameworks)
{
if (fw.StartsWith("net4")
- && RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
+ && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
&& Environment.GetEnvironmentVariable("FORCE_LINUX_TESTS") != "1")
{
- Information($"Skipping {projectName} ({fw}) tests on Linux - https://github.com/mono/mono/issues/13969");
+ Information($"Skipping {projectName} ({fw}) tests on *nix - https://github.com/mono/mono/issues/13969");
continue;
}
@@ -220,16 +220,18 @@ partial class Build : NukeBuild
.Executes(() =>
{
RunCoreTest("Avalonia.Skia.RenderTests");
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (Parameters.IsRunningOnWindows)
RunCoreTest("Avalonia.Direct2D1.RenderTests");
});
- Target RunDesignerTests => _ => _
- .OnlyWhenStatic(() => !Parameters.SkipTests && Parameters.IsRunningOnWindows)
+ Target RunToolsTests => _ => _
+ .OnlyWhenStatic(() => !Parameters.SkipTests)
.DependsOn(Compile)
.Executes(() =>
{
- RunCoreTest("Avalonia.DesignerSupport.Tests");
+ RunCoreTest("Avalonia.Generators.Tests");
+ if (Parameters.IsRunningOnWindows)
+ RunCoreTest("Avalonia.DesignerSupport.Tests");
});
Target RunLeakTests => _ => _
@@ -276,7 +278,7 @@ partial class Build : NukeBuild
Target RunTests => _ => _
.DependsOn(RunCoreLibsTests)
.DependsOn(RunRenderTests)
- .DependsOn(RunDesignerTests)
+ .DependsOn(RunToolsTests)
.DependsOn(RunHtmlPreviewerTests)
.DependsOn(RunLeakTests);
diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs
index 5fd331035a..f2dd217657 100644
--- a/nukebuild/BuildTasksPatcher.cs
+++ b/nukebuild/BuildTasksPatcher.cs
@@ -4,9 +4,58 @@ using System.IO.Compression;
using System.Linq;
using ILRepacking;
using Mono.Cecil;
+using Mono.Cecil.Cil;
public class BuildTasksPatcher
{
+ ///
+ /// This helper class, avoid argument null exception
+ /// when cecil write AssemblyNameDefinition on MemoryStream.
+ ///
+ private class Wrapper : ISymbolWriterProvider
+ {
+ readonly ISymbolWriterProvider _provider;
+ readonly string _filename;
+
+ public Wrapper(ISymbolWriterProvider provider, string filename)
+ {
+ _provider = provider;
+ _filename = filename;
+ }
+
+ public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName) =>
+ _provider.GetSymbolWriter(module, string.IsNullOrWhiteSpace(fileName) ? _filename : fileName);
+
+ public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream) =>
+ _provider.GetSymbolWriter(module, symbolStream);
+ }
+
+ private static string GetSourceLinkInfo(string path)
+ {
+ try
+ {
+ using (var asm = AssemblyDefinition.ReadAssembly(path,
+ new ReaderParameters
+ {
+ ReadWrite = true,
+ InMemory = true,
+ ReadSymbols = true,
+ SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
+ }))
+ {
+ if (asm.MainModule.CustomDebugInformations?.OfType()?.FirstOrDefault() is { } sli)
+ {
+ return sli.Content;
+ }
+ }
+ }
+ catch
+ {
+
+ }
+ return null;
+ }
+
public static void PatchBuildTasksInPackage(string packagePath)
{
using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
@@ -19,7 +68,7 @@ public class BuildTasksPatcher
{
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempDir);
- var temp = Path.Combine(tempDir, Guid.NewGuid() + ".dll");
+ var temp = Path.Combine(tempDir, entry.Name);
var output = temp + ".output";
File.Copy(typeof(Microsoft.Build.Framework.ITask).Assembly.GetModules()[0].FullyQualifiedName,
Path.Combine(tempDir, "Microsoft.Build.Framework.dll"));
@@ -27,41 +76,74 @@ public class BuildTasksPatcher
try
{
entry.ExtractToFile(temp, true);
+ // Get Original SourceLinkInfo Content
+ var sourceLinkInfoContent = GetSourceLinkInfo(temp);
var repack = new ILRepacking.ILRepack(new RepackOptions()
{
Internalize = true,
InputAssemblies = new[]
{
- temp, typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0]
- .FullyQualifiedName,
+ temp,
+ typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0].FullyQualifiedName,
typeof(Mono.Cecil.Rocks.MethodBodyRocks).Assembly.GetModules()[0].FullyQualifiedName,
typeof(Mono.Cecil.Pdb.PdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName,
- typeof(Mono.Cecil.Mdb.MdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName
-
+ typeof(Mono.Cecil.Mdb.MdbReaderProvider).Assembly.GetModules()[0].FullyQualifiedName,
},
- SearchDirectories = new string[0],
+ SearchDirectories = Array.Empty(),
+ DebugInfo = true, // Allowed read debug info
OutputFile = output
});
repack.Repack();
-
// 'hurr-durr assembly with the same name is already loaded' prevention
using (var asm = AssemblyDefinition.ReadAssembly(output,
- new ReaderParameters { ReadWrite = true, InMemory = true, }))
+ new ReaderParameters
+ {
+ ReadWrite = true,
+ InMemory = true,
+ ReadSymbols = true,
+ SymbolReaderProvider = new DefaultSymbolReaderProvider(false),
+ }))
{
asm.Name = new AssemblyNameDefinition(
"Avalonia.Build.Tasks."
+ Guid.NewGuid().ToString().Replace("-", ""),
new Version(0, 0, 0));
- asm.Write(patched);
+
+ var mainModule = asm.MainModule;
+
+ // If we have SourceLink info copy to patched assembly.
+ if (!string.IsNullOrEmpty(sourceLinkInfoContent))
+ {
+ mainModule.CustomDebugInformations.Add(new SourceLinkDebugInformation(sourceLinkInfoContent));
+ }
+
+ // Try to get SymbolWriter if it has it
+ var reader = mainModule.SymbolReader;
+ var hasDebugInfo = reader is not null;
+ var proivder = reader?.GetWriterProvider() is ISymbolWriterProvider p
+ ? new Wrapper(p, "Avalonia.Build.Tasks.dll")
+ : default(ISymbolWriterProvider);
+
+ var parameters = new WriterParameters
+ {
+#if ISNETFULLFRAMEWORK
+ StrongNameKeyPair = signingStep.KeyPair,
+#endif
+ WriteSymbols = hasDebugInfo,
+ SymbolWriterProvider = proivder,
+ DeterministicMvid = hasDebugInfo,
+ };
+ asm.Write(patched, parameters);
patched.Position = 0;
}
+
}
finally
{
try
{
- if(Directory.Exists(tempDir))
+ if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
catch
@@ -79,4 +161,4 @@ public class BuildTasksPatcher
}
}
}
-}
\ No newline at end of file
+}
diff --git a/nukebuild/numerge.config b/nukebuild/numerge.config
index d1c0408241..09f22ec527 100644
--- a/nukebuild/numerge.config
+++ b/nukebuild/numerge.config
@@ -11,6 +11,11 @@
"Id": "Avalonia.Build.Tasks",
"IgnoreMissingFrameworkBinaries": true,
"DoNotMergeDependencies": true
+ },
+ {
+ "Id": "Avalonia.Generators",
+ "IgnoreMissingFrameworkBinaries": true,
+ "DoNotMergeDependencies": true
}
]
}
diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj
index 4d0ed866a3..f21d6fefb4 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -6,11 +6,15 @@
-
+
all
true
TargetFramework=netstandard2.0
+
@@ -39,6 +43,10 @@
true
build\;buildTransitive\
+
+ true
+ build\;buildTransitive\
+
true
build\;buildTransitive\
diff --git a/packages/Avalonia/Avalonia.props b/packages/Avalonia/Avalonia.props
index 6f21971d3d..2334aa91bc 100644
--- a/packages/Avalonia/Avalonia.props
+++ b/packages/Avalonia/Avalonia.props
@@ -6,4 +6,10 @@
false
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
index 2600cf83cc..c8135080fe 100644
--- a/readme.md
+++ b/readme.md
@@ -5,6 +5,10 @@
[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) 
+# ⚠️ **v11 Update - Pausing community contributions**
+
+for more information see [this](https://github.com/AvaloniaUI/Avalonia/discussions/10599) discussion.
+
## 📖 About
Avalonia is a cross-platform UI framework for dotnet, providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, macOS. Avalonia is mature and production ready. We also have in beta release support for iOS, Android and in early stages support for browser via WASM.
diff --git a/samples/BindingDemo/BindingDemo.csproj b/samples/BindingDemo/BindingDemo.csproj
index 056d3bf552..f094c0081c 100644
--- a/samples/BindingDemo/BindingDemo.csproj
+++ b/samples/BindingDemo/BindingDemo.csproj
@@ -5,6 +5,7 @@
+
diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml
index 08ac0426ea..d1c65ca73b 100644
--- a/samples/BindingDemo/MainWindow.xaml
+++ b/samples/BindingDemo/MainWindow.xaml
@@ -75,11 +75,11 @@
-
+
-
+
diff --git a/samples/ControlCatalog.Android/MainActivity.cs b/samples/ControlCatalog.Android/MainActivity.cs
index f6fa07dbde..486d14661e 100644
--- a/samples/ControlCatalog.Android/MainActivity.cs
+++ b/samples/ControlCatalog.Android/MainActivity.cs
@@ -5,7 +5,7 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
- [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ [Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.Main", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
public class MainActivity : AvaloniaMainActivity
{
}
diff --git a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml
index 6f551d2b01..ec07a94b22 100644
--- a/samples/ControlCatalog.Android/Properties/AndroidManifest.xml
+++ b/samples/ControlCatalog.Android/Properties/AndroidManifest.xml
@@ -2,4 +2,6 @@
+
+
diff --git a/samples/ControlCatalog.Android/Resources/values-night/colors.xml b/samples/ControlCatalog.Android/Resources/values-night/colors.xml
new file mode 100644
index 0000000000..3d47b6fc58
--- /dev/null
+++ b/samples/ControlCatalog.Android/Resources/values-night/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #212121
+
diff --git a/samples/ControlCatalog.Android/Resources/values/styles.xml b/samples/ControlCatalog.Android/Resources/values/styles.xml
index 49e079a719..3e1270256d 100644
--- a/samples/ControlCatalog.Android/Resources/values/styles.xml
+++ b/samples/ControlCatalog.Android/Resources/values/styles.xml
@@ -4,7 +4,7 @@
-
diff --git a/samples/ControlCatalog.Browser/app.css b/samples/ControlCatalog.Browser/app.css
index 27680f6e1a..0e6ab12461 100644
--- a/samples/ControlCatalog.Browser/app.css
+++ b/samples/ControlCatalog.Browser/app.css
@@ -1,4 +1,11 @@
-#out {
+:root {
+ --sat: env(safe-area-inset-top);
+ --sar: env(safe-area-inset-right);
+ --sab: env(safe-area-inset-bottom);
+ --sal: env(safe-area-inset-left);
+}
+
+#out {
height: 100vh;
width: 100vw
}
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index e55f003133..85c159467b 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Fonts.Inter;
using Avalonia.Headless;
using Avalonia.LogicalTree;
using Avalonia.Threading;
@@ -124,6 +125,7 @@ namespace ControlCatalog.NetCore
EnableIme = true
})
.UseSkia()
+ .WithInterFont()
.AfterSetup(builder =>
{
builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions()
diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
index 74d5b2fd8c..b4dac5399c 100644
--- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
+++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj
@@ -3,7 +3,7 @@
Exe
manual
net6.0-ios
- 10.0
+ 13.0
True
iossimulator-x64
@@ -16,4 +16,4 @@
-
\ No newline at end of file
+
diff --git a/samples/ControlCatalog.iOS/Info.plist b/samples/ControlCatalog.iOS/Info.plist
index 6ffe3ba662..1dd4416c28 100644
--- a/samples/ControlCatalog.iOS/Info.plist
+++ b/samples/ControlCatalog.iOS/Info.plist
@@ -13,7 +13,7 @@
LSRequiresIPhoneOS
MinimumOSVersion
- 10.0
+ 13.0
UIDeviceFamily
1
@@ -39,9 +39,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIStatusBarHidden
-
- UIViewControllerBasedStatusBarAppearance
-
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index d71d51f068..246fe4385f 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -44,11 +44,11 @@ namespace ControlCatalog
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
- desktopLifetime.MainWindow = new MainWindow();
+ desktopLifetime.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() };
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
{
- singleViewLifetime.MainView = new MainView();
+ singleViewLifetime.MainView = new MainView { DataContext = new MainWindowViewModel() };
}
base.OnFrameworkInitializationCompleted();
diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj
index c223bfe1a9..5125b42426 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -2,7 +2,8 @@
netstandard2.0;net6.0
true
- enable
+ enable
+ true
@@ -29,19 +30,11 @@
+
-
-
-
-
-
-
-
-
-
-
+
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 3681298a72..7ed2d67379 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -147,9 +147,6 @@
-
-
-
@@ -241,7 +238,7 @@
diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs
index 6f31d22677..9c439c874f 100644
--- a/samples/ControlCatalog/MainView.xaml.cs
+++ b/samples/ControlCatalog/MainView.xaml.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
@@ -12,6 +13,7 @@ using Avalonia.VisualTree;
using Avalonia.Styling;
using ControlCatalog.Models;
using ControlCatalog.Pages;
+using ControlCatalog.ViewModels;
namespace ControlCatalog
{
@@ -99,13 +101,47 @@ namespace ControlCatalog
};
}
+ internal MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!;
+
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
+
var decorations = this.Get("Decorations");
if (VisualRoot is Window window)
decorations.SelectedIndex = (int)window.SystemDecorations;
-
+
+ var insets = TopLevel.GetTopLevel(this)!.InsetsManager;
+ if (insets != null)
+ {
+ // In real life application these events should be unsubscribed to avoid memory leaks.
+ ViewModel.SafeAreaPadding = insets.SafeAreaPadding;
+ insets.SafeAreaChanged += (sender, args) =>
+ {
+ ViewModel.SafeAreaPadding = insets.SafeAreaPadding;
+ };
+
+ ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdge;
+ ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
+
+ ViewModel.PropertyChanged += async (sender, args) =>
+ {
+ if (args.PropertyName == nameof(ViewModel.DisplayEdgeToEdge))
+ {
+ insets.DisplayEdgeToEdge = ViewModel.DisplayEdgeToEdge;
+ }
+ else if (args.PropertyName == nameof(ViewModel.IsSystemBarVisible))
+ {
+ insets.IsSystemBarVisible = ViewModel.IsSystemBarVisible;
+ }
+
+ // Give the OS some time to apply new values and refresh the view model.
+ await Task.Delay(100);
+ ViewModel.DisplayEdgeToEdge = insets.DisplayEdgeToEdge;
+ ViewModel.IsSystemBarVisible = insets.IsSystemBarVisible ?? true;
+ };
+ }
+
_platformSettings.ColorValuesChanged += PlatformSettingsOnColorValuesChanged;
PlatformSettingsOnColorValuesChanged(_platformSettings, _platformSettings.GetColorValues());
}
diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs
index c589f41442..10ff94d25c 100644
--- a/samples/ControlCatalog/MainWindow.xaml.cs
+++ b/samples/ControlCatalog/MainWindow.xaml.cs
@@ -17,7 +17,6 @@ namespace ControlCatalog
{
this.InitializeComponent();
- DataContext = new MainWindowViewModel();
_recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu;
}
diff --git a/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs b/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
index ef3d2bbafa..089d1af197 100644
--- a/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
@@ -1,16 +1,23 @@
using System;
using System.Collections.Generic;
-
+using System.Linq;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Notifications;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
+using Avalonia.Platform;
+using Avalonia.Platform.Storage;
+using Avalonia.Platform.Storage.FileIO;
namespace ControlCatalog.Pages
{
public partial class ClipboardPage : UserControl
{
+ private INotificationManager? _notificationManager;
+ private INotificationManager NotificationManager => _notificationManager
+ ??= new WindowNotificationManager(TopLevel.GetTopLevel(this)!);
public ClipboardPage()
{
InitializeComponent();
@@ -25,13 +32,13 @@ namespace ControlCatalog.Pages
private async void CopyText(object? sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard && ClipboardContent is { } clipboardContent)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard && ClipboardContent is { } clipboardContent)
await clipboard.SetTextAsync(clipboardContent.Text ?? String.Empty);
}
private async void PasteText(object? sender, RoutedEventArgs args)
{
- if(Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
ClipboardContent.Text = await clipboard.GetTextAsync();
}
@@ -39,7 +46,7 @@ namespace ControlCatalog.Pages
private async void CopyTextDataObject(object? sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
@@ -49,7 +56,7 @@ namespace ControlCatalog.Pages
private async void PasteTextDataObject(object? sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
ClipboardContent.Text = await clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
@@ -57,32 +64,63 @@ namespace ControlCatalog.Pages
private async void CopyFilesDataObject(object? sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
- var files = (ClipboardContent.Text ?? String.Empty)
- .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
- if (files.Length == 0)
+ var storageProvider = TopLevel.GetTopLevel(this)!.StorageProvider;
+ var filesPath = (ClipboardContent.Text ?? string.Empty)
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+ if (filesPath.Length == 0)
{
return;
}
- var dataObject = new DataObject();
- dataObject.Set(DataFormats.FileNames, files);
- await clipboard.SetDataObjectAsync(dataObject);
+ List invalidFile = new(filesPath.Length);
+ List files = new(filesPath.Length);
+
+ for (int i = 0; i < filesPath.Length; i++)
+ {
+ var file = await storageProvider.TryGetFileFromPathAsync(filesPath[i]);
+ if (file is null)
+ {
+ invalidFile.Add(filesPath[i]);
+ }
+ else
+ {
+ files.Add(file);
+ }
+ }
+
+ if (invalidFile.Count > 0)
+ {
+ NotificationManager.Show(new Notification("Warning", "There is one o more invalid path.", NotificationType.Warning));
+ }
+
+ if (files.Count > 0)
+ {
+ var dataObject = new DataObject();
+ dataObject.Set(DataFormats.Files, files);
+ await clipboard.SetDataObjectAsync(dataObject);
+ NotificationManager.Show(new Notification("Success", "Copy completated.", NotificationType.Success));
+ }
+ else
+ {
+ NotificationManager.Show(new Notification("Warning", "Any files to copy in Clipboard.", NotificationType.Warning));
+ }
}
}
private async void PasteFilesDataObject(object? sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
- var fiels = await clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable;
- ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
+ var files = await clipboard.GetDataAsync(DataFormats.Files) as IEnumerable;
+
+ ClipboardContent.Text = files != null ? string.Join(Environment.NewLine, files.Select(f => f.TryGetLocalPath() ?? f.Name)) : string.Empty;
}
}
private async void GetFormats(object sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
var formats = await clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
@@ -91,11 +129,11 @@ namespace ControlCatalog.Pages
private async void Clear(object sender, RoutedEventArgs args)
{
- if (Application.Current!.Clipboard is { } clipboard)
+ if (TopLevel.GetTopLevel(this)?.Clipboard is { } clipboard)
{
await clipboard.ClearAsync();
}
-
+
}
}
}
diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
index 649256ba83..b759720cf2 100644
--- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml
+++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml
@@ -28,6 +28,41 @@
+
+
+ 24
+ 18
+ 12
+ 9
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
("fontComboBox");
- fontComboBox.Items = FontManager.Current.GetInstalledFontFamilyNames().Select(x => new FontFamily(x));
+ fontComboBox.ItemsSource = FontManager.Current.SystemFonts;
fontComboBox.SelectedIndex = 0;
}
}
diff --git a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
index 8b12a2d663..0d3061f361 100644
--- a/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
+++ b/samples/ControlCatalog/Pages/CompositionPage.axaml.cs
@@ -32,7 +32,7 @@ public partial class CompositionPage : UserControl
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
- this.Get("Items").Items = CreateColorItems();
+ this.Get("Items").ItemsSource = CreateColorItems();
}
diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
index 6ef6a202b6..cb294442d2 100644
--- a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
+++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
@@ -61,7 +61,7 @@
diff --git a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
index 8bd1f4d85a..0388eee4e2 100644
--- a/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
@@ -36,8 +36,9 @@ namespace ControlCatalog.Pages
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.Get("CancellableContextBorder");
- cancellableContextBorder.ContextFlyout!.Closing += ContextFlyoutPage_Closing;
- cancellableContextBorder.ContextFlyout!.Opening += ContextFlyoutPage_Opening;
+ var flyout = (Flyout)cancellableContextBorder.ContextFlyout!;
+ flyout.Closing += ContextFlyoutPage_Closing;
+ flyout.Opening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel? _model;
diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
index 962f0308f7..06eba52605 100644
--- a/samples/ControlCatalog/Pages/ContextMenuPage.xaml
+++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml
@@ -51,13 +51,13 @@
-
+
diff --git a/samples/ControlCatalog/Pages/CursorPage.xaml b/samples/ControlCatalog/Pages/CursorPage.xaml
index 30bad06d72..66f2b8b2e3 100644
--- a/samples/ControlCatalog/Pages/CursorPage.xaml
+++ b/samples/ControlCatalog/Pages/CursorPage.xaml
@@ -8,7 +8,7 @@
Defines a cursor (mouse pointer)
-
+
@@ -68,7 +68,7 @@
-