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.sln b/Avalonia.sln
index e66b73de0e..b21df07628 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -244,8 +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
@@ -573,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
+ {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
@@ -643,7 +661,10 @@ Global
{75C47156-C5D8-44BC-A5A7-E8657C2248D6} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C810060E-3809-4B74-A125-F11533AF9C1B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{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 7221fe4657..939d09c959 100644
--- a/azure-pipelines-integrationtests.yml
+++ b/azure-pipelines-integrationtests.yml
@@ -15,28 +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:
@@ -60,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..5440b76bfe 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:@""];
+
+ if(!_parent->InputMethod->IsActive()){
+ return;
+ }
+
+ _parent->InputMethod->Client->SetPreeditText(nullptr);
+
+ [[self inputContext] discardMarkedText];
}
- (NSArray *)validAttributesForMarkedText
@@ -590,30 +614,38 @@
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- return [NSAttributedString new];
+ return nullptr;
}
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- if(!_lastKeyHandled)
- {
+ //[_text replaceCharactersInRange:replacementRange withString:string];
+
+ [self unmarkText];
+
+ //if(!_lastKeyHandled)
+ //{
if(_parent != nullptr)
{
_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 +750,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..104611eabc 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);
}
}
@@ -617,5 +622,5 @@ void WindowImpl::UpdateStyle() {
[miniaturizeButton setHidden:!hasTrafficLights];
[miniaturizeButton setEnabled:_isEnabled];
[zoomButton setHidden:!hasTrafficLights];
- [zoomButton setEnabled:_isEnabled && _canResize];
+ [zoomButton setEnabled:CanZoom()];
}
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/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..1d210172f0 100644
--- a/packages/Avalonia/Avalonia.csproj
+++ b/packages/Avalonia/Avalonia.csproj
@@ -6,11 +6,15 @@
-
+
all
true
TargetFramework=netstandard2.0
+
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/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 0e84b3d182..5125b42426 100644
--- a/samples/ControlCatalog/ControlCatalog.csproj
+++ b/samples/ControlCatalog/ControlCatalog.csproj
@@ -2,7 +2,8 @@
netstandard2.0;net6.0
true
- enable
+ enable
+ true
@@ -35,14 +36,5 @@
-
-
-
-
-
-
-
-
-
-
+
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/ComboBoxPage.xaml.cs b/samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
index 6d624c9a07..6d759597b5 100644
--- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
@@ -18,7 +18,7 @@ namespace ControlCatalog.Pages
{
AvaloniaXamlLoader.Load(this);
var fontComboBox = this.Get("fontComboBox");
- fontComboBox.Items = FontManager.Current.GetInstalledFontFamilyNames().Select(x => new FontFamily(x));
+ fontComboBox.Items = FontManager.Current.SystemFonts;
fontComboBox.SelectedIndex = 0;
}
}
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/FlyoutsPage.axaml b/samples/ControlCatalog/Pages/FlyoutsPage.axaml
index 54aa9d1b67..c0521cd3ba 100644
--- a/samples/ControlCatalog/Pages/FlyoutsPage.axaml
+++ b/samples/ControlCatalog/Pages/FlyoutsPage.axaml
@@ -16,8 +16,13 @@
+
+
+
+
+
-
+
@@ -136,6 +141,15 @@
+
-