1569 changed files with 62383 additions and 52482 deletions
@ -1,9 +1,11 @@ |
|||||
<Project> |
<Project> |
||||
|
<Import Project="$(MSBuildThisFileDirectory)/build/AvaloniaPublicKey.props"/> |
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath> |
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath> |
||||
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> |
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> |
||||
<!-- https://github.com/dotnet/msbuild/issues/2661 --> |
<!-- https://github.com/dotnet/msbuild/issues/2661 --> |
||||
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies> |
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies> |
||||
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver> |
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver> |
||||
|
<RunApiCompat>False</RunApiCompat> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,2 @@ |
|||||
|
T:Avalonia.Metadata.NotClientImplementableAttribute |
||||
|
T:Avalonia.Metadata.UnstableAttribute |
||||
@ -0,0 +1,5 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<AvaloniaPublicKey>0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87</AvaloniaPublicKey> |
||||
|
</PropertyGroup> |
||||
|
</Project> |
||||
@ -1,7 +1,7 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.254" /> |
<PackageReference Include="HarfBuzzSharp" Version="2.8.2" /> |
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.254" /> |
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2" /> |
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.254"/> |
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,5 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -1,5 +1,5 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="JetBrains.DotMemoryUnit" Version="3.1.20200127.214830" /> |
<PackageReference Include="JetBrains.DotMemoryUnit" Version="3.2.20220510" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,5 +0,0 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|
||||
<ItemGroup> |
|
||||
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.9.0.2" /> |
|
||||
</ItemGroup> |
|
||||
</Project> |
|
||||
@ -1,7 +1,6 @@ |
|||||
<Project> |
<Project> |
||||
|
|
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,7 +1,7 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.254" /> |
<PackageReference Include="SkiaSharp" Version="2.88.0" /> |
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.254" /> |
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0" /> |
||||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.254"/> |
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,10 @@ |
|||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference |
||||
|
Include="$(MSBuildThisFileDirectory)/../src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj" |
||||
|
OutputItemType="Analyzer" |
||||
|
ReferenceOutputAssembly="false" |
||||
|
PrivateAssets="all" /> |
||||
|
<Compile Include="$(MSBuildThisFileDirectory)/../src/Shared/SourceGeneratorAttributes.cs" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,17 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 05/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#include "avalonia-native.h" |
||||
|
|
||||
|
@interface AutoFitContentView : NSView |
||||
|
-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; |
||||
|
-(void) ShowTitleBar: (bool) show; |
||||
|
-(void) SetTitleBarHeightHint: (double) height; |
||||
|
|
||||
|
-(void) ShowBlur: (bool) show; |
||||
|
@end |
||||
@ -0,0 +1,106 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 05/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#include "AvnView.h" |
||||
|
#include "AutoFitContentView.h" |
||||
|
#include "WindowInterfaces.h" |
||||
|
#include "WindowProtocol.h" |
||||
|
|
||||
|
@implementation AutoFitContentView |
||||
|
{ |
||||
|
NSVisualEffectView* _titleBarMaterial; |
||||
|
NSBox* _titleBarUnderline; |
||||
|
NSView* _content; |
||||
|
NSVisualEffectView* _blurBehind; |
||||
|
double _titleBarHeightHint; |
||||
|
bool _settingSize; |
||||
|
} |
||||
|
|
||||
|
-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content |
||||
|
{ |
||||
|
_titleBarHeightHint = -1; |
||||
|
_content = content; |
||||
|
_settingSize = false; |
||||
|
|
||||
|
[self setAutoresizesSubviews:true]; |
||||
|
[self setWantsLayer:true]; |
||||
|
|
||||
|
_titleBarMaterial = [NSVisualEffectView new]; |
||||
|
[_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow]; |
||||
|
[_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar]; |
||||
|
[_titleBarMaterial setWantsLayer:true]; |
||||
|
_titleBarMaterial.hidden = true; |
||||
|
|
||||
|
_titleBarUnderline = [NSBox new]; |
||||
|
_titleBarUnderline.boxType = NSBoxSeparator; |
||||
|
_titleBarUnderline.fillColor = [NSColor underPageBackgroundColor]; |
||||
|
_titleBarUnderline.hidden = true; |
||||
|
|
||||
|
[self addSubview:_titleBarMaterial]; |
||||
|
[self addSubview:_titleBarUnderline]; |
||||
|
|
||||
|
_blurBehind = [NSVisualEffectView new]; |
||||
|
[_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; |
||||
|
[_blurBehind setMaterial:NSVisualEffectMaterialLight]; |
||||
|
[_blurBehind setWantsLayer:true]; |
||||
|
_blurBehind.hidden = true; |
||||
|
|
||||
|
[_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; |
||||
|
[_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; |
||||
|
|
||||
|
[self addSubview:_blurBehind]; |
||||
|
[self addSubview:_content]; |
||||
|
|
||||
|
[self setWantsLayer:true]; |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
-(void) ShowBlur:(bool)show |
||||
|
{ |
||||
|
_blurBehind.hidden = !show; |
||||
|
} |
||||
|
|
||||
|
-(void) ShowTitleBar: (bool) show |
||||
|
{ |
||||
|
_titleBarMaterial.hidden = !show; |
||||
|
_titleBarUnderline.hidden = !show; |
||||
|
} |
||||
|
|
||||
|
-(void) SetTitleBarHeightHint: (double) height |
||||
|
{ |
||||
|
_titleBarHeightHint = height; |
||||
|
|
||||
|
[self setFrameSize:self.frame.size]; |
||||
|
} |
||||
|
|
||||
|
-(void)setFrameSize:(NSSize)newSize |
||||
|
{ |
||||
|
if(_settingSize) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_settingSize = true; |
||||
|
[super setFrameSize:newSize]; |
||||
|
|
||||
|
auto window = (id <AvnWindowProtocol>) [self window]; |
||||
|
|
||||
|
// TODO get actual titlebar size |
||||
|
|
||||
|
double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint; |
||||
|
|
||||
|
NSRect tbar; |
||||
|
tbar.origin.x = 0; |
||||
|
tbar.origin.y = newSize.height - height; |
||||
|
tbar.size.width = newSize.width; |
||||
|
tbar.size.height = height; |
||||
|
|
||||
|
[_titleBarMaterial setFrame:tbar]; |
||||
|
tbar.size.height = height < 1 ? 0 : 1; |
||||
|
[_titleBarUnderline setFrame:tbar]; |
||||
|
|
||||
|
_settingSize = false; |
||||
|
} |
||||
|
@end |
||||
@ -0,0 +1,11 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 06/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#define IS_NSPANEL |
||||
|
|
||||
|
#include "AvnWindow.mm" |
||||
|
|
||||
@ -0,0 +1,27 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 05/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
#pragma once |
||||
|
#import <Foundation/Foundation.h> |
||||
|
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "common.h" |
||||
|
#include "WindowImpl.h" |
||||
|
#include "KeyTransform.h" |
||||
|
|
||||
|
@class AvnAccessibilityElement; |
||||
|
|
||||
|
@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination> |
||||
|
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; |
||||
|
-(NSEvent* _Nonnull) lastMouseDownEvent; |
||||
|
-(AvnPoint) translateLocalPoint:(AvnPoint)pt; |
||||
|
-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; |
||||
|
-(void) onClosed; |
||||
|
|
||||
|
-(AvnPlatformResizeReason) getResizeReason; |
||||
|
-(void) setResizeReason:(AvnPlatformResizeReason)reason; |
||||
|
+ (AvnPoint)toAvnPoint:(CGPoint)p; |
||||
|
@end |
||||
@ -0,0 +1,712 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 05/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "AvnView.h" |
||||
|
#include "automation.h" |
||||
|
#import "WindowInterfaces.h" |
||||
|
|
||||
|
@implementation AvnView |
||||
|
{ |
||||
|
ComPtr<WindowBaseImpl> _parent; |
||||
|
NSTrackingArea* _area; |
||||
|
bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; |
||||
|
AvnInputModifiers _modifierState; |
||||
|
NSEvent* _lastMouseDownEvent; |
||||
|
bool _lastKeyHandled; |
||||
|
AvnPixelSize _lastPixelSize; |
||||
|
NSObject<IRenderTarget>* _renderTarget; |
||||
|
AvnPlatformResizeReason _resizeReason; |
||||
|
AvnAccessibilityElement* _accessibilityChild; |
||||
|
} |
||||
|
|
||||
|
- (void)onClosed |
||||
|
{ |
||||
|
@synchronized (self) |
||||
|
{ |
||||
|
_parent = nullptr; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (NSEvent*) lastMouseDownEvent |
||||
|
{ |
||||
|
return _lastMouseDownEvent; |
||||
|
} |
||||
|
|
||||
|
- (void) updateRenderTarget |
||||
|
{ |
||||
|
[_renderTarget resize:_lastPixelSize withScale:static_cast<float>([[self window] backingScaleFactor])]; |
||||
|
[self setNeedsDisplayInRect:[self frame]]; |
||||
|
} |
||||
|
|
||||
|
-(AvnView*) initWithParent: (WindowBaseImpl*) parent |
||||
|
{ |
||||
|
self = [super init]; |
||||
|
_renderTarget = parent->renderTarget; |
||||
|
[self setWantsLayer:YES]; |
||||
|
[self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; |
||||
|
|
||||
|
_parent = parent; |
||||
|
_area = nullptr; |
||||
|
_lastPixelSize.Height = 100; |
||||
|
_lastPixelSize.Width = 100; |
||||
|
[self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; |
||||
|
|
||||
|
_modifierState = AvnInputModifiersNone; |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)isFlipped |
||||
|
{ |
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)wantsUpdateLayer |
||||
|
{ |
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
- (void)setLayer:(CALayer *)layer |
||||
|
{ |
||||
|
[_renderTarget setNewLayer: layer]; |
||||
|
[super setLayer: layer]; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)isOpaque |
||||
|
{ |
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)acceptsFirstResponder |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)acceptsFirstMouse:(NSEvent *)event |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)canBecomeKeyView |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
-(void)setFrameSize:(NSSize)newSize |
||||
|
{ |
||||
|
[super setFrameSize:newSize]; |
||||
|
|
||||
|
if(_area != nullptr) |
||||
|
{ |
||||
|
[self removeTrackingArea:_area]; |
||||
|
_area = nullptr; |
||||
|
} |
||||
|
|
||||
|
if (_parent == nullptr) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
NSRect rect = NSZeroRect; |
||||
|
rect.size = newSize; |
||||
|
|
||||
|
NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag; |
||||
|
_area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; |
||||
|
[self addTrackingArea:_area]; |
||||
|
|
||||
|
_parent->UpdateCursor(); |
||||
|
|
||||
|
auto fsize = [self convertSizeToBacking: [self frame].size]; |
||||
|
|
||||
|
if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) |
||||
|
{ |
||||
|
_lastPixelSize.Width = (int)fsize.width; |
||||
|
_lastPixelSize.Height = (int)fsize.height; |
||||
|
[self updateRenderTarget]; |
||||
|
|
||||
|
auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; |
||||
|
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)updateLayer |
||||
|
{ |
||||
|
AvnInsidePotentialDeadlock deadlock; |
||||
|
if (_parent == nullptr) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_parent->BaseEvents->RunRenderPriorityJobs(); |
||||
|
|
||||
|
if (_parent == nullptr) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_parent->BaseEvents->Paint(); |
||||
|
} |
||||
|
|
||||
|
- (void)drawRect:(NSRect)dirtyRect |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose |
||||
|
{ |
||||
|
@autoreleasepool { |
||||
|
[_renderTarget setSwFrame:fb]; |
||||
|
dispose->Release(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (AvnPoint) translateLocalPoint:(AvnPoint)pt |
||||
|
{ |
||||
|
pt.Y = [self bounds].size.height - pt.Y; |
||||
|
return pt; |
||||
|
} |
||||
|
|
||||
|
+ (AvnPoint)toAvnPoint:(CGPoint)p |
||||
|
{ |
||||
|
AvnPoint result; |
||||
|
|
||||
|
result.X = p.x; |
||||
|
result.Y = p.y; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
- (void) viewDidChangeBackingProperties |
||||
|
{ |
||||
|
auto fsize = [self convertSizeToBacking: [self frame].size]; |
||||
|
_lastPixelSize.Width = (int)fsize.width; |
||||
|
_lastPixelSize.Height = (int)fsize.height; |
||||
|
[self updateRenderTarget]; |
||||
|
|
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
_parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); |
||||
|
} |
||||
|
|
||||
|
[super viewDidChangeBackingProperties]; |
||||
|
} |
||||
|
|
||||
|
- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled |
||||
|
{ |
||||
|
if(_parent == nullptr) |
||||
|
{ |
||||
|
return TRUE; |
||||
|
} |
||||
|
|
||||
|
auto parentWindow = _parent->GetWindowProtocol(); |
||||
|
|
||||
|
if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) |
||||
|
{ |
||||
|
if(trigerInputWhenDisabled) |
||||
|
{ |
||||
|
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw()); |
||||
|
|
||||
|
if(window != nullptr) |
||||
|
{ |
||||
|
window->WindowEvents->GotInputWhenDisabled(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return TRUE; |
||||
|
} |
||||
|
|
||||
|
return FALSE; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type |
||||
|
{ |
||||
|
bool triggerInputWhenDisabled = type != Move; |
||||
|
|
||||
|
if([self ignoreUserInput: triggerInputWhenDisabled]) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; |
||||
|
auto avnPoint = [AvnView toAvnPoint:localPoint]; |
||||
|
auto point = [self translateLocalPoint:avnPoint]; |
||||
|
AvnVector delta = { 0, 0}; |
||||
|
|
||||
|
if(type == Wheel) |
||||
|
{ |
||||
|
auto speed = 5; |
||||
|
|
||||
|
if([event hasPreciseScrollingDeltas]) |
||||
|
{ |
||||
|
speed = 50; |
||||
|
} |
||||
|
|
||||
|
delta.X = [event scrollingDeltaX] / speed; |
||||
|
delta.Y = [event scrollingDeltaY] / speed; |
||||
|
|
||||
|
if(delta.X == 0 && delta.Y == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
else if (type == Magnify) |
||||
|
{ |
||||
|
delta.X = delta.Y = [event magnification]; |
||||
|
} |
||||
|
else if (type == Rotate) |
||||
|
{ |
||||
|
delta.X = delta.Y = [event rotation]; |
||||
|
} |
||||
|
else if (type == Swipe) |
||||
|
{ |
||||
|
delta.X = [event deltaX]; |
||||
|
delta.Y = [event deltaY]; |
||||
|
} |
||||
|
|
||||
|
uint32 timestamp = static_cast<uint32>([event timestamp] * 1000); |
||||
|
auto modifiers = [self getModifiers:[event modifierFlags]]; |
||||
|
|
||||
|
if(type != Move || |
||||
|
( |
||||
|
[self window] != nil && |
||||
|
( |
||||
|
[[self window] firstResponder] == nil |
||||
|
|| ![[[self window] firstResponder] isKindOfClass: [NSView class]] |
||||
|
) |
||||
|
) |
||||
|
) |
||||
|
[self becomeFirstResponder]; |
||||
|
|
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
_parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); |
||||
|
} |
||||
|
|
||||
|
[super mouseMoved:event]; |
||||
|
} |
||||
|
|
||||
|
- (BOOL) resignFirstResponder |
||||
|
{ |
||||
|
_parent->BaseEvents->LostFocus(); |
||||
|
return YES; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseMoved:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Move]; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseDown:(NSEvent *)event |
||||
|
{ |
||||
|
_isLeftPressed = true; |
||||
|
_lastMouseDownEvent = event; |
||||
|
[self mouseEvent:event withType:LeftButtonDown]; |
||||
|
} |
||||
|
|
||||
|
- (void)otherMouseDown:(NSEvent *)event |
||||
|
{ |
||||
|
_lastMouseDownEvent = event; |
||||
|
|
||||
|
switch(event.buttonNumber) |
||||
|
{ |
||||
|
case 2: |
||||
|
case 3: |
||||
|
_isMiddlePressed = true; |
||||
|
[self mouseEvent:event withType:MiddleButtonDown]; |
||||
|
break; |
||||
|
case 4: |
||||
|
_isXButton1Pressed = true; |
||||
|
[self mouseEvent:event withType:XButton1Down]; |
||||
|
break; |
||||
|
case 5: |
||||
|
_isXButton2Pressed = true; |
||||
|
[self mouseEvent:event withType:XButton2Down]; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)rightMouseDown:(NSEvent *)event |
||||
|
{ |
||||
|
_isRightPressed = true; |
||||
|
_lastMouseDownEvent = event; |
||||
|
[self mouseEvent:event withType:RightButtonDown]; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseUp:(NSEvent *)event |
||||
|
{ |
||||
|
_isLeftPressed = false; |
||||
|
[self mouseEvent:event withType:LeftButtonUp]; |
||||
|
} |
||||
|
|
||||
|
- (void)otherMouseUp:(NSEvent *)event |
||||
|
{ |
||||
|
switch(event.buttonNumber) |
||||
|
{ |
||||
|
case 2: |
||||
|
case 3: |
||||
|
_isMiddlePressed = false; |
||||
|
[self mouseEvent:event withType:MiddleButtonUp]; |
||||
|
break; |
||||
|
case 4: |
||||
|
_isXButton1Pressed = false; |
||||
|
[self mouseEvent:event withType:XButton1Up]; |
||||
|
break; |
||||
|
case 5: |
||||
|
_isXButton2Pressed = false; |
||||
|
[self mouseEvent:event withType:XButton2Up]; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)rightMouseUp:(NSEvent *)event |
||||
|
{ |
||||
|
_isRightPressed = false; |
||||
|
[self mouseEvent:event withType:RightButtonUp]; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseDragged:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Move]; |
||||
|
[super mouseDragged:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)otherMouseDragged:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Move]; |
||||
|
[super otherMouseDragged:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)rightMouseDragged:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Move]; |
||||
|
[super rightMouseDragged:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)scrollWheel:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Wheel]; |
||||
|
[super scrollWheel:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)magnifyWithEvent:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Magnify]; |
||||
|
[super magnifyWithEvent:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)rotateWithEvent:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Rotate]; |
||||
|
[super rotateWithEvent:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)swipeWithEvent:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:Swipe]; |
||||
|
[super swipeWithEvent:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseEntered:(NSEvent *)event |
||||
|
{ |
||||
|
[super mouseEntered:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)mouseExited:(NSEvent *)event |
||||
|
{ |
||||
|
[self mouseEvent:event withType:LeaveWindow]; |
||||
|
[super mouseExited:event]; |
||||
|
} |
||||
|
|
||||
|
- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type |
||||
|
{ |
||||
|
if([self ignoreUserInput: false]) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
auto key = s_KeyMap[[event keyCode]]; |
||||
|
|
||||
|
uint32_t timestamp = static_cast<uint32_t>([event timestamp] * 1000); |
||||
|
auto modifiers = [self getModifiers:[event modifierFlags]]; |
||||
|
|
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
_lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (BOOL)performKeyEquivalent:(NSEvent *)event |
||||
|
{ |
||||
|
bool result = _lastKeyHandled; |
||||
|
|
||||
|
_lastKeyHandled = false; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
- (void)flagsChanged:(NSEvent *)event |
||||
|
{ |
||||
|
auto newModifierState = [self getModifiers:[event modifierFlags]]; |
||||
|
|
||||
|
bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt; |
||||
|
bool isControlCurrentlyPressed = (_modifierState & Control) == Control; |
||||
|
bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift; |
||||
|
bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows; |
||||
|
|
||||
|
bool isAltPressed = (newModifierState & Alt) == Alt; |
||||
|
bool isControlPressed = (newModifierState & Control) == Control; |
||||
|
bool isShiftPressed = (newModifierState & Shift) == Shift; |
||||
|
bool isCommandPressed = (newModifierState & Windows) == Windows; |
||||
|
|
||||
|
|
||||
|
if (isAltPressed && !isAltCurrentlyPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyDown]; |
||||
|
} |
||||
|
else if (isAltCurrentlyPressed && !isAltPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyUp]; |
||||
|
} |
||||
|
|
||||
|
if (isControlPressed && !isControlCurrentlyPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyDown]; |
||||
|
} |
||||
|
else if (isControlCurrentlyPressed && !isControlPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyUp]; |
||||
|
} |
||||
|
|
||||
|
if (isShiftPressed && !isShiftCurrentlyPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyDown]; |
||||
|
} |
||||
|
else if(isShiftCurrentlyPressed && !isShiftPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyUp]; |
||||
|
} |
||||
|
|
||||
|
if(isCommandPressed && !isCommandCurrentlyPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyDown]; |
||||
|
} |
||||
|
else if(isCommandCurrentlyPressed && ! isCommandPressed) |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyUp]; |
||||
|
} |
||||
|
|
||||
|
_modifierState = newModifierState; |
||||
|
|
||||
|
[[self inputContext] handleEvent:event]; |
||||
|
[super flagsChanged:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)keyDown:(NSEvent *)event |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyDown]; |
||||
|
[[self inputContext] handleEvent:event]; |
||||
|
[super keyDown:event]; |
||||
|
} |
||||
|
|
||||
|
- (void)keyUp:(NSEvent *)event |
||||
|
{ |
||||
|
[self keyboardEvent:event withType:KeyUp]; |
||||
|
[super keyUp:event]; |
||||
|
} |
||||
|
|
||||
|
- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod |
||||
|
{ |
||||
|
unsigned int rv = 0; |
||||
|
|
||||
|
if (mod & NSEventModifierFlagControl) |
||||
|
rv |= Control; |
||||
|
if (mod & NSEventModifierFlagShift) |
||||
|
rv |= Shift; |
||||
|
if (mod & NSEventModifierFlagOption) |
||||
|
rv |= Alt; |
||||
|
if (mod & NSEventModifierFlagCommand) |
||||
|
rv |= Windows; |
||||
|
|
||||
|
if (_isLeftPressed) |
||||
|
rv |= LeftMouseButton; |
||||
|
if (_isMiddlePressed) |
||||
|
rv |= MiddleMouseButton; |
||||
|
if (_isRightPressed) |
||||
|
rv |= RightMouseButton; |
||||
|
if (_isXButton1Pressed) |
||||
|
rv |= XButton1MouseButton; |
||||
|
if (_isXButton2Pressed) |
||||
|
rv |= XButton2MouseButton; |
||||
|
|
||||
|
return (AvnInputModifiers)rv; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)hasMarkedText |
||||
|
{ |
||||
|
return _lastKeyHandled; |
||||
|
} |
||||
|
|
||||
|
- (NSRange)markedRange |
||||
|
{ |
||||
|
return NSMakeRange(NSNotFound, 0); |
||||
|
} |
||||
|
|
||||
|
- (NSRange)selectedRange |
||||
|
{ |
||||
|
return NSMakeRange(NSNotFound, 0); |
||||
|
} |
||||
|
|
||||
|
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
- (void)unmarkText |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
- (NSArray<NSString *> *)validAttributesForMarkedText |
||||
|
{ |
||||
|
return [NSArray new]; |
||||
|
} |
||||
|
|
||||
|
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange |
||||
|
{ |
||||
|
return [NSAttributedString new]; |
||||
|
} |
||||
|
|
||||
|
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange |
||||
|
{ |
||||
|
if(!_lastKeyHandled) |
||||
|
{ |
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
_lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (NSUInteger)characterIndexForPoint:(NSPoint)point |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange |
||||
|
{ |
||||
|
CGRect result = { 0 }; |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id <NSDraggingInfo>)info |
||||
|
{ |
||||
|
auto localPoint = [self convertPoint:[info draggingLocation] toView:self]; |
||||
|
auto avnPoint = [AvnView toAvnPoint:localPoint]; |
||||
|
auto point = [self translateLocalPoint:avnPoint]; |
||||
|
auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]]; |
||||
|
NSDragOperation nsop = [info draggingSourceOperationMask]; |
||||
|
|
||||
|
auto effects = ConvertDragDropEffects(nsop); |
||||
|
int reffects = (int)_parent->BaseEvents |
||||
|
->DragEvent(type, point, modifiers, effects, |
||||
|
CreateClipboard([info draggingPasteboard], nil), |
||||
|
GetAvnDataObjectHandleFromDraggingInfo(info)); |
||||
|
|
||||
|
NSDragOperation ret = static_cast<NSDragOperation>(0); |
||||
|
|
||||
|
// Ensure that the managed part didn't add any new effects |
||||
|
reffects = (int)effects & reffects; |
||||
|
|
||||
|
// OSX requires exactly one operation |
||||
|
if((reffects & (int)AvnDragDropEffects::Copy) != 0) |
||||
|
ret = NSDragOperationCopy; |
||||
|
else if((reffects & (int)AvnDragDropEffects::Move) != 0) |
||||
|
ret = NSDragOperationMove; |
||||
|
else if((reffects & (int)AvnDragDropEffects::Link) != 0) |
||||
|
ret = NSDragOperationLink; |
||||
|
if(ret == 0) |
||||
|
ret = NSDragOperationNone; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender]; |
||||
|
} |
||||
|
|
||||
|
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender]; |
||||
|
} |
||||
|
|
||||
|
- (void)draggingExited:(id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
[self triggerAvnDragEvent: AvnDragEventType::Leave info:sender]; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone; |
||||
|
} |
||||
|
|
||||
|
- (void)concludeDragOperation:(nullable id <NSDraggingInfo>)sender |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
- (AvnPlatformResizeReason)getResizeReason |
||||
|
{ |
||||
|
return _resizeReason; |
||||
|
} |
||||
|
|
||||
|
- (void)setResizeReason:(AvnPlatformResizeReason)reason |
||||
|
{ |
||||
|
_resizeReason = reason; |
||||
|
} |
||||
|
|
||||
|
- (AvnAccessibilityElement *) accessibilityChild |
||||
|
{ |
||||
|
if (_accessibilityChild == nil) |
||||
|
{ |
||||
|
auto peer = _parent->BaseEvents->GetAutomationPeer(); |
||||
|
|
||||
|
if (peer == nil) |
||||
|
return nil; |
||||
|
|
||||
|
_accessibilityChild = [AvnAccessibilityElement acquire:peer]; |
||||
|
} |
||||
|
|
||||
|
return _accessibilityChild; |
||||
|
} |
||||
|
|
||||
|
- (NSArray *)accessibilityChildren |
||||
|
{ |
||||
|
auto child = [self accessibilityChild]; |
||||
|
return NSAccessibilityUnignoredChildrenForOnlyChild(child); |
||||
|
} |
||||
|
|
||||
|
- (id)accessibilityHitTest:(NSPoint)point |
||||
|
{ |
||||
|
return [[self accessibilityChild] accessibilityHitTest:point]; |
||||
|
} |
||||
|
|
||||
|
- (id)accessibilityFocusedUIElement |
||||
|
{ |
||||
|
return [[self accessibilityChild] accessibilityFocusedUIElement]; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
@ -0,0 +1,466 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 06/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#import "WindowProtocol.h" |
||||
|
#import "WindowBaseImpl.h" |
||||
|
|
||||
|
#ifdef IS_NSPANEL |
||||
|
#define BASE_CLASS NSPanel |
||||
|
#define CLASS_NAME AvnPanel |
||||
|
#else |
||||
|
#define BASE_CLASS NSWindow |
||||
|
#define CLASS_NAME AvnWindow |
||||
|
#endif |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "common.h" |
||||
|
#include "menu.h" |
||||
|
#include "automation.h" |
||||
|
#include "WindowBaseImpl.h" |
||||
|
#include "WindowImpl.h" |
||||
|
#include "AvnView.h" |
||||
|
#include "WindowInterfaces.h" |
||||
|
#include "PopupImpl.h" |
||||
|
|
||||
|
@implementation CLASS_NAME |
||||
|
{ |
||||
|
ComPtr<WindowBaseImpl> _parent; |
||||
|
bool _closed; |
||||
|
bool _isEnabled; |
||||
|
bool _canBecomeKeyWindow; |
||||
|
bool _isExtended; |
||||
|
AvnMenu* _menu; |
||||
|
} |
||||
|
|
||||
|
-(void) setIsExtended:(bool)value; |
||||
|
{ |
||||
|
_isExtended = value; |
||||
|
} |
||||
|
|
||||
|
-(bool) isDialog |
||||
|
{ |
||||
|
return _parent->IsDialog(); |
||||
|
} |
||||
|
|
||||
|
-(double) getExtendedTitleBarHeight |
||||
|
{ |
||||
|
if(_isExtended) |
||||
|
{ |
||||
|
for (id subview in self.contentView.superview.subviews) |
||||
|
{ |
||||
|
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) |
||||
|
{ |
||||
|
NSView *titlebarView = [subview subviews][0]; |
||||
|
|
||||
|
return (double)titlebarView.frame.size.height; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return -1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)performClose:(id)sender |
||||
|
{ |
||||
|
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) |
||||
|
{ |
||||
|
if(![[self delegate] windowShouldClose:self]) return; |
||||
|
} |
||||
|
else if([self respondsToSelector:@selector(windowShouldClose:)]) |
||||
|
{ |
||||
|
if(![self windowShouldClose:self]) return; |
||||
|
} |
||||
|
|
||||
|
[self close]; |
||||
|
} |
||||
|
|
||||
|
- (void)pollModalSession:(nonnull NSModalSession)session |
||||
|
{ |
||||
|
auto response = [NSApp runModalSession:session]; |
||||
|
|
||||
|
if(response == NSModalResponseContinue) |
||||
|
{ |
||||
|
dispatch_async(dispatch_get_main_queue(), ^{ |
||||
|
[self pollModalSession:session]; |
||||
|
}); |
||||
|
} |
||||
|
else if (!_closed) |
||||
|
{ |
||||
|
[self orderOut:self]; |
||||
|
[NSApp endModalSession:session]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
-(void) showWindowMenuWithAppMenu |
||||
|
{ |
||||
|
if(_menu != nullptr) |
||||
|
{ |
||||
|
auto appMenuItem = ::GetAppMenuItem(); |
||||
|
|
||||
|
if(appMenuItem != nullptr) |
||||
|
{ |
||||
|
auto appMenu = [appMenuItem menu]; |
||||
|
|
||||
|
[appMenu removeItem:appMenuItem]; |
||||
|
|
||||
|
[_menu insertItem:appMenuItem atIndex:0]; |
||||
|
|
||||
|
[_menu setHasGlobalMenuItem:true]; |
||||
|
} |
||||
|
|
||||
|
[NSApp setMenu:_menu]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
[self showAppMenuOnly]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
-(void) showAppMenuOnly |
||||
|
{ |
||||
|
auto appMenuItem = ::GetAppMenuItem(); |
||||
|
|
||||
|
if(appMenuItem != nullptr) |
||||
|
{ |
||||
|
auto appMenu = ::GetAppMenu(); |
||||
|
|
||||
|
auto nativeAppMenu = dynamic_cast<AvnAppMenu*>(appMenu); |
||||
|
|
||||
|
[[appMenuItem menu] removeItem:appMenuItem]; |
||||
|
|
||||
|
if(_menu != nullptr) |
||||
|
{ |
||||
|
[_menu setHasGlobalMenuItem:false]; |
||||
|
} |
||||
|
|
||||
|
[nativeAppMenu->GetNative() addItem:appMenuItem]; |
||||
|
|
||||
|
[NSApp setMenu:nativeAppMenu->GetNative()]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
-(void) applyMenu:(AvnMenu *)menu |
||||
|
{ |
||||
|
if(menu == nullptr) |
||||
|
{ |
||||
|
menu = [AvnMenu new]; |
||||
|
} |
||||
|
|
||||
|
_menu = menu; |
||||
|
} |
||||
|
|
||||
|
-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; |
||||
|
{ |
||||
|
// https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ |
||||
|
// create nswindow with specific contentRect, otherwise we wont be able to resize the window |
||||
|
// until several ms after the window is physically on the screen. |
||||
|
self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; |
||||
|
|
||||
|
[self setReleasedWhenClosed:false]; |
||||
|
_parent = parent; |
||||
|
[self setDelegate:self]; |
||||
|
_closed = false; |
||||
|
_isEnabled = true; |
||||
|
|
||||
|
[self backingScaleFactor]; |
||||
|
[self setOpaque:NO]; |
||||
|
[self setBackgroundColor: [NSColor clearColor]]; |
||||
|
|
||||
|
_isExtended = false; |
||||
|
|
||||
|
#ifdef IS_NSPANEL |
||||
|
[self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorFullScreenAuxiliary]; |
||||
|
#endif |
||||
|
|
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
- (BOOL)windowShouldClose:(NSWindow *)sender |
||||
|
{ |
||||
|
auto window = dynamic_cast<WindowImpl*>(_parent.getRaw()); |
||||
|
|
||||
|
if(window != nullptr) |
||||
|
{ |
||||
|
return !window->WindowEvents->Closing(); |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidChangeBackingProperties:(NSNotification *)notification |
||||
|
{ |
||||
|
[self backingScaleFactor]; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
- (void)windowWillClose:(NSNotification *)notification |
||||
|
{ |
||||
|
_closed = true; |
||||
|
if(_parent) |
||||
|
{ |
||||
|
ComPtr<WindowBaseImpl> parent = _parent; |
||||
|
_parent = NULL; |
||||
|
[self restoreParentWindow]; |
||||
|
parent->BaseEvents->Closed(); |
||||
|
[parent->View onClosed]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
-(BOOL)canBecomeKeyWindow |
||||
|
{ |
||||
|
if(_canBecomeKeyWindow) |
||||
|
{ |
||||
|
// If the window has a child window being shown as a dialog then don't allow it to become the key window. |
||||
|
for(NSWindow* uch in [self childWindows]) |
||||
|
{ |
||||
|
if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
id <AvnWindowProtocol> ch = (id <AvnWindowProtocol>) uch; |
||||
|
|
||||
|
if(ch.isDialog) |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
#ifndef IS_NSPANEL |
||||
|
-(BOOL)canBecomeMainWindow |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
-(void)setCanBecomeKeyWindow:(bool)value |
||||
|
{ |
||||
|
_canBecomeKeyWindow = value; |
||||
|
} |
||||
|
|
||||
|
-(bool)shouldTryToHandleEvents |
||||
|
{ |
||||
|
return _isEnabled; |
||||
|
} |
||||
|
|
||||
|
-(void) setEnabled:(bool)enable |
||||
|
{ |
||||
|
_isEnabled = enable; |
||||
|
} |
||||
|
|
||||
|
-(void)becomeKeyWindow |
||||
|
{ |
||||
|
[self showWindowMenuWithAppMenu]; |
||||
|
|
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
_parent->BaseEvents->Activated(); |
||||
|
} |
||||
|
|
||||
|
[super becomeKeyWindow]; |
||||
|
} |
||||
|
|
||||
|
-(void) restoreParentWindow; |
||||
|
{ |
||||
|
auto parent = [self parentWindow]; |
||||
|
|
||||
|
if(parent != nil) |
||||
|
{ |
||||
|
[parent removeChildWindow:self]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidMiniaturize:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->WindowStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidDeminiaturize:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->WindowStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidResize:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->WindowStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowWillExitFullScreen:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->StartStateTransition(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidExitFullScreen:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->EndStateTransition(); |
||||
|
|
||||
|
if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized) |
||||
|
{ |
||||
|
NSRect screenRect = [[self screen] visibleFrame]; |
||||
|
[self setFrame:screenRect display:YES]; |
||||
|
} |
||||
|
|
||||
|
if(parent->WindowState() == Minimized) |
||||
|
{ |
||||
|
[self miniaturize:nullptr]; |
||||
|
} |
||||
|
|
||||
|
parent->WindowStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowWillEnterFullScreen:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->StartStateTransition(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidEnterFullScreen:(NSNotification *)notification |
||||
|
{ |
||||
|
auto parent = dynamic_cast<IWindowStateChanged*>(_parent.operator->()); |
||||
|
|
||||
|
if(parent != nullptr) |
||||
|
{ |
||||
|
parent->EndStateTransition(); |
||||
|
parent->WindowStateChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
-(void)resignKeyWindow |
||||
|
{ |
||||
|
if(_parent) |
||||
|
_parent->BaseEvents->Deactivated(); |
||||
|
|
||||
|
[self showAppMenuOnly]; |
||||
|
|
||||
|
[super resignKeyWindow]; |
||||
|
} |
||||
|
|
||||
|
- (void)windowDidMove:(NSNotification *)notification |
||||
|
{ |
||||
|
AvnPoint position; |
||||
|
|
||||
|
if(_parent != nullptr) |
||||
|
{ |
||||
|
auto cparent = dynamic_cast<WindowImpl*>(_parent.getRaw()); |
||||
|
|
||||
|
if(cparent != nullptr) |
||||
|
{ |
||||
|
if(!cparent->IsShown()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if(cparent->WindowState() == Maximized) |
||||
|
{ |
||||
|
cparent->SetWindowState(Normal); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_parent->GetPosition(&position); |
||||
|
_parent->BaseEvents->PositionChanged(position); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (AvnPoint) translateLocalPoint:(AvnPoint)pt |
||||
|
{ |
||||
|
pt.Y = [self frame].size.height - pt.Y; |
||||
|
return pt; |
||||
|
} |
||||
|
|
||||
|
- (void)sendEvent:(NSEvent *)event |
||||
|
{ |
||||
|
[super sendEvent:event]; |
||||
|
|
||||
|
/// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast. |
||||
|
if(_parent != nullptr && dynamic_cast<WindowImpl*>(_parent.getRaw()) != nullptr) |
||||
|
{ |
||||
|
switch(event.type) |
||||
|
{ |
||||
|
case NSEventTypeLeftMouseDown: |
||||
|
{ |
||||
|
AvnView* view = _parent->View; |
||||
|
NSPoint windowPoint = [event locationInWindow]; |
||||
|
NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; |
||||
|
|
||||
|
if (!NSPointInRect(viewPoint, view.bounds)) |
||||
|
{ |
||||
|
auto avnPoint = [AvnView toAvnPoint:windowPoint]; |
||||
|
auto point = [self translateLocalPoint:avnPoint]; |
||||
|
AvnVector delta = { 0, 0 }; |
||||
|
|
||||
|
_parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast<uint32>([event timestamp] * 1000), AvnInputModifiersNone, point, delta); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case NSEventTypeMouseEntered: |
||||
|
{ |
||||
|
_parent->UpdateCursor(); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case NSEventTypeMouseExited: |
||||
|
{ |
||||
|
[[NSCursor arrowCursor] set]; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
- (void)disconnectParent { |
||||
|
_parent = nullptr; |
||||
|
} |
||||
|
|
||||
|
@end |
||||
|
|
||||
@ -0,0 +1,17 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 04/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H |
||||
|
#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H |
||||
|
|
||||
|
@class AvnView; |
||||
|
|
||||
|
struct INSWindowHolder |
||||
|
{ |
||||
|
virtual NSWindow* _Nonnull GetNSWindow () = 0; |
||||
|
virtual NSView* _Nonnull GetNSView () = 0; |
||||
|
}; |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H
|
||||
@ -0,0 +1,18 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 04/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H |
||||
|
#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H |
||||
|
|
||||
|
struct IWindowStateChanged |
||||
|
{ |
||||
|
virtual void WindowStateChanged () = 0; |
||||
|
virtual void StartStateTransition () = 0; |
||||
|
virtual void EndStateTransition () = 0; |
||||
|
virtual SystemDecorations Decorations () = 0; |
||||
|
virtual AvnWindowState WindowState () = 0; |
||||
|
}; |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H
|
||||
@ -0,0 +1,9 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 06/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H |
||||
|
#define AVALONIA_NATIVE_OSX_POPUPIMPL_H |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H
|
||||
@ -0,0 +1,61 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 06/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#include "WindowInterfaces.h" |
||||
|
#include "AvnView.h" |
||||
|
#include "WindowImpl.h" |
||||
|
#include "automation.h" |
||||
|
#include "menu.h" |
||||
|
#include "common.h" |
||||
|
#import "WindowBaseImpl.h" |
||||
|
#import "WindowProtocol.h" |
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "PopupImpl.h" |
||||
|
|
||||
|
class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup |
||||
|
{ |
||||
|
private: |
||||
|
BEGIN_INTERFACE_MAP() |
||||
|
INHERIT_INTERFACE_MAP(WindowBaseImpl) |
||||
|
INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) |
||||
|
END_INTERFACE_MAP() |
||||
|
virtual ~PopupImpl(){} |
||||
|
ComPtr<IAvnWindowEvents> WindowEvents; |
||||
|
PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) |
||||
|
{ |
||||
|
WindowEvents = events; |
||||
|
} |
||||
|
protected: |
||||
|
virtual NSWindowStyleMask GetStyle() override |
||||
|
{ |
||||
|
return NSWindowStyleMaskBorderless; |
||||
|
} |
||||
|
|
||||
|
virtual void OnInitialiseNSWindow () override |
||||
|
{ |
||||
|
[Window setLevel:NSPopUpMenuWindowLevel]; |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
virtual bool ShouldTakeFocusOnShow() override |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
virtual HRESULT Show(bool activate, bool isDialog) override |
||||
|
{ |
||||
|
return WindowBaseImpl::Show(activate, true); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl)); |
||||
|
return ptr; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 04/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H |
||||
|
#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H |
||||
|
|
||||
|
#include "avalonia-native.h" |
||||
|
|
||||
|
@class AvnView; |
||||
|
|
||||
|
class ResizeScope |
||||
|
{ |
||||
|
public: |
||||
|
ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason); |
||||
|
|
||||
|
~ResizeScope(); |
||||
|
private: |
||||
|
AvnView* _Nonnull _view; |
||||
|
AvnPlatformResizeReason _restore; |
||||
|
}; |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H
|
||||
@ -0,0 +1,18 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 04/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "ResizeScope.h" |
||||
|
#include "AvnView.h" |
||||
|
|
||||
|
ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { |
||||
|
_view = view; |
||||
|
_restore = [view getResizeReason]; |
||||
|
[view setResizeReason:reason]; |
||||
|
} |
||||
|
|
||||
|
ResizeScope::~ResizeScope() { |
||||
|
[_view setResizeReason:_restore]; |
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 04/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H |
||||
|
#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H |
||||
|
|
||||
|
#include "rendertarget.h" |
||||
|
#include "INSWindowHolder.h" |
||||
|
|
||||
|
@class AutoFitContentView; |
||||
|
@class AvnMenu; |
||||
|
@protocol AvnWindowProtocol; |
||||
|
|
||||
|
class WindowBaseImpl : public virtual ComObject, |
||||
|
public virtual IAvnWindowBase, |
||||
|
public INSWindowHolder { |
||||
|
|
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
|
||||
|
BEGIN_INTERFACE_MAP() |
||||
|
INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) |
||||
|
END_INTERFACE_MAP() |
||||
|
|
||||
|
virtual ~WindowBaseImpl(); |
||||
|
|
||||
|
WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); |
||||
|
|
||||
|
virtual HRESULT ObtainNSWindowHandle(void **ret) override; |
||||
|
|
||||
|
virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override; |
||||
|
|
||||
|
virtual HRESULT ObtainNSViewHandle(void **ret) override; |
||||
|
|
||||
|
virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; |
||||
|
|
||||
|
virtual NSWindow *GetNSWindow() override; |
||||
|
|
||||
|
virtual NSView *GetNSView() override; |
||||
|
|
||||
|
virtual HRESULT Show(bool activate, bool isDialog) override; |
||||
|
|
||||
|
virtual bool IsShown (); |
||||
|
|
||||
|
virtual bool ShouldTakeFocusOnShow(); |
||||
|
|
||||
|
virtual HRESULT Hide() override; |
||||
|
|
||||
|
virtual HRESULT Activate() override; |
||||
|
|
||||
|
virtual HRESULT SetTopMost(bool value) override; |
||||
|
|
||||
|
virtual HRESULT Close() override; |
||||
|
|
||||
|
virtual HRESULT GetClientSize(AvnSize *ret) override; |
||||
|
|
||||
|
virtual HRESULT GetFrameSize(AvnSize *ret) override; |
||||
|
|
||||
|
virtual HRESULT GetScaling(double *ret) override; |
||||
|
|
||||
|
virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override; |
||||
|
|
||||
|
virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; |
||||
|
|
||||
|
virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override; |
||||
|
|
||||
|
virtual HRESULT SetMainMenu(IAvnMenu *menu) override; |
||||
|
|
||||
|
virtual HRESULT BeginMoveDrag() override; |
||||
|
|
||||
|
virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override; |
||||
|
|
||||
|
virtual HRESULT GetPosition(AvnPoint *ret) override; |
||||
|
|
||||
|
virtual HRESULT SetPosition(AvnPoint point) override; |
||||
|
|
||||
|
virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; |
||||
|
|
||||
|
virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; |
||||
|
|
||||
|
virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override; |
||||
|
|
||||
|
virtual HRESULT SetCursor(IAvnCursor *cursor) override; |
||||
|
|
||||
|
virtual void UpdateCursor(); |
||||
|
|
||||
|
virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override; |
||||
|
|
||||
|
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; |
||||
|
|
||||
|
virtual HRESULT SetBlurEnabled(bool enable) override; |
||||
|
|
||||
|
virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, |
||||
|
IAvnClipboard *clipboard, IAvnDndResultCallback *cb, |
||||
|
void *sourceHandle) override; |
||||
|
|
||||
|
virtual bool IsDialog(); |
||||
|
|
||||
|
id<AvnWindowProtocol> GetWindowProtocol (); |
||||
|
|
||||
|
protected: |
||||
|
virtual NSWindowStyleMask GetStyle(); |
||||
|
|
||||
|
void UpdateStyle(); |
||||
|
|
||||
|
virtual void OnInitialiseNSWindow (); |
||||
|
|
||||
|
private: |
||||
|
void CreateNSWindow (bool isDialog); |
||||
|
void CleanNSWindow (); |
||||
|
void InitialiseNSWindow (); |
||||
|
|
||||
|
NSCursor *cursor; |
||||
|
ComPtr<IAvnGlContext> _glContext; |
||||
|
bool hasPosition; |
||||
|
NSSize lastSize; |
||||
|
NSSize lastMinSize; |
||||
|
NSSize lastMaxSize; |
||||
|
AvnMenu* lastMenu; |
||||
|
bool _inResize; |
||||
|
|
||||
|
protected: |
||||
|
AvnPoint lastPositionSet; |
||||
|
AutoFitContentView *StandardContainer; |
||||
|
bool _shown; |
||||
|
|
||||
|
public: |
||||
|
NSObject <IRenderTarget> *renderTarget; |
||||
|
NSWindow * Window; |
||||
|
ComPtr<IAvnWindowBaseEvents> BaseEvents; |
||||
|
AvnView *View; |
||||
|
}; |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H
|
||||
@ -0,0 +1,618 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 04/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "common.h" |
||||
|
#include "AvnView.h" |
||||
|
#include "menu.h" |
||||
|
#include "automation.h" |
||||
|
#include "cursor.h" |
||||
|
#include "ResizeScope.h" |
||||
|
#include "AutoFitContentView.h" |
||||
|
#import "WindowProtocol.h" |
||||
|
#import "WindowInterfaces.h" |
||||
|
#include "WindowBaseImpl.h" |
||||
|
|
||||
|
|
||||
|
WindowBaseImpl::~WindowBaseImpl() { |
||||
|
View = nullptr; |
||||
|
Window = nullptr; |
||||
|
} |
||||
|
|
||||
|
WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { |
||||
|
_shown = false; |
||||
|
_inResize = false; |
||||
|
BaseEvents = events; |
||||
|
_glContext = gl; |
||||
|
renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; |
||||
|
View = [[AvnView alloc] initWithParent:this]; |
||||
|
StandardContainer = [[AutoFitContentView new] initWithContent:View]; |
||||
|
|
||||
|
lastPositionSet = { 0, 0 }; |
||||
|
hasPosition = false; |
||||
|
lastSize = NSSize { 100, 100 }; |
||||
|
lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; |
||||
|
lastMinSize = NSSize { 0, 0 }; |
||||
|
|
||||
|
Window = nullptr; |
||||
|
lastMenu = nullptr; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge void *) View; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge_retained void *) View; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
NSWindow *WindowBaseImpl::GetNSWindow() { |
||||
|
return Window; |
||||
|
} |
||||
|
|
||||
|
NSView *WindowBaseImpl::GetNSView() { |
||||
|
return View; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge_retained void *) Window; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
CreateNSWindow(isDialog); |
||||
|
InitialiseNSWindow(); |
||||
|
|
||||
|
if(hasPosition) |
||||
|
{ |
||||
|
SetPosition(lastPositionSet); |
||||
|
} else |
||||
|
{ |
||||
|
[Window center]; |
||||
|
} |
||||
|
|
||||
|
UpdateStyle(); |
||||
|
|
||||
|
if (ShouldTakeFocusOnShow() && activate) { |
||||
|
[Window orderFront:Window]; |
||||
|
[Window makeKeyAndOrderFront:Window]; |
||||
|
[Window makeFirstResponder:View]; |
||||
|
[NSApp activateIgnoringOtherApps:YES]; |
||||
|
} else { |
||||
|
[Window orderFront:Window]; |
||||
|
} |
||||
|
|
||||
|
_shown = true; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool WindowBaseImpl::IsShown () |
||||
|
{ |
||||
|
return _shown; |
||||
|
} |
||||
|
|
||||
|
bool WindowBaseImpl::ShouldTakeFocusOnShow() { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge void *) Window; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Hide() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (Window != nullptr) { |
||||
|
[Window orderOut:Window]; |
||||
|
|
||||
|
[GetWindowProtocol() restoreParentWindow]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Activate() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (Window != nullptr) { |
||||
|
[Window makeKeyAndOrderFront:nil]; |
||||
|
[NSApp activateIgnoringOtherApps:YES]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetTopMost(bool value) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
[Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Close() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (Window != nullptr) { |
||||
|
auto window = Window; |
||||
|
Window = nullptr; |
||||
|
|
||||
|
try { |
||||
|
// Seems to throw sometimes on application exit. |
||||
|
[window close]; |
||||
|
} |
||||
|
catch (NSException *) {} |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
ret->Width = lastSize.width; |
||||
|
ret->Height = lastSize.height; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
if(Window != nullptr){ |
||||
|
auto frame = [Window frame]; |
||||
|
ret->Width = frame.size.width; |
||||
|
ret->Height = frame.size.height; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::GetScaling(double *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
if (Window == nullptr) { |
||||
|
*ret = 1; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
*ret = [Window backingScaleFactor]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
lastMinSize = ToNSSize(minSize); |
||||
|
lastMaxSize = ToNSSize(maxSize); |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
[Window setContentMinSize:lastMinSize]; |
||||
|
[Window setContentMaxSize:lastMaxSize]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) { |
||||
|
if (_inResize) { |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
_inResize = true; |
||||
|
|
||||
|
START_COM_CALL; |
||||
|
auto resizeBlock = ResizeScope(View, reason); |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
auto maxSize = lastMaxSize; |
||||
|
auto minSize = lastMinSize; |
||||
|
|
||||
|
if (x < minSize.width) { |
||||
|
x = minSize.width; |
||||
|
} |
||||
|
|
||||
|
if (y < minSize.height) { |
||||
|
y = minSize.height; |
||||
|
} |
||||
|
|
||||
|
if (x > maxSize.width) { |
||||
|
x = maxSize.width; |
||||
|
} |
||||
|
|
||||
|
if (y > maxSize.height) { |
||||
|
y = maxSize.height; |
||||
|
} |
||||
|
|
||||
|
@try { |
||||
|
lastSize = NSSize {x, y}; |
||||
|
|
||||
|
if (!_shown) { |
||||
|
BaseEvents->Resized(AvnSize{x, y}, reason); |
||||
|
} |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
[Window setContentSize:lastSize]; |
||||
|
[Window invalidateShadow]; |
||||
|
} |
||||
|
} |
||||
|
@finally { |
||||
|
_inResize = false; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
[View setNeedsDisplayInRect:[View frame]]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
auto nativeMenu = dynamic_cast<AvnAppMenu *>(menu); |
||||
|
|
||||
|
lastMenu = nativeMenu->GetNative(); |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
[GetWindowProtocol() applyMenu:lastMenu]; |
||||
|
|
||||
|
if ([Window isKeyWindow]) { |
||||
|
[GetWindowProtocol() showWindowMenuWithAppMenu]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::BeginMoveDrag() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
auto lastEvent = [View lastMouseDownEvent]; |
||||
|
|
||||
|
if (lastEvent == nullptr) { |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
[Window performWindowDragWithEvent:lastEvent]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
auto frame = [Window frame]; |
||||
|
|
||||
|
ret->X = frame.origin.x; |
||||
|
ret->Y = frame.origin.y + frame.size.height; |
||||
|
|
||||
|
*ret = ConvertPointY(*ret); |
||||
|
} else |
||||
|
{ |
||||
|
*ret = lastPositionSet; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
lastPositionSet = point; |
||||
|
hasPosition = true; |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
[Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
point = ConvertPointY(point); |
||||
|
NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; |
||||
|
auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); |
||||
|
|
||||
|
*ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); |
||||
|
NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; |
||||
|
auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); |
||||
|
*ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
[View setSwRenderedFrame:fb dispose:dispose]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
Cursor *avnCursor = dynamic_cast<Cursor *>(cursor); |
||||
|
this->cursor = avnCursor->GetNative(); |
||||
|
UpdateCursor(); |
||||
|
|
||||
|
if (avnCursor->IsHidden()) { |
||||
|
[NSCursor hide]; |
||||
|
} else { |
||||
|
[NSCursor unhide]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::UpdateCursor() { |
||||
|
if (cursor != nil) { |
||||
|
[cursor set]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
*ppv = [renderTarget createSurfaceRenderTarget]; |
||||
|
return static_cast<HRESULT>(*ppv == nil ? E_FAIL : S_OK); |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
*retOut = ::CreateNativeControlHost(View); |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
[StandardContainer ShowBlur:enable]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
auto item = TryGetPasteboardItem(clipboard); |
||||
|
[item setString:@"" forType:GetAvnCustomDataType()]; |
||||
|
if (item == nil) |
||||
|
return E_INVALIDARG; |
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
|
||||
|
auto nsevent = [NSApp currentEvent]; |
||||
|
auto nseventType = [nsevent type]; |
||||
|
|
||||
|
// If current event isn't a mouse one (probably due to malfunctioning user app) |
||||
|
// attempt to forge a new one |
||||
|
if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) |
||||
|
|| (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) { |
||||
|
NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; |
||||
|
auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); |
||||
|
CGPoint cgpoint = NSPointToCGPoint(nspoint); |
||||
|
auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); |
||||
|
nsevent = [NSEvent eventWithCGEvent:cgevent]; |
||||
|
CFRelease(cgevent); |
||||
|
} |
||||
|
|
||||
|
auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item]; |
||||
|
|
||||
|
auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; |
||||
|
NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height}; |
||||
|
[dragItem setDraggingFrame:dragItemRect contents:dragItemImage]; |
||||
|
|
||||
|
int op = 0; |
||||
|
int ieffects = (int) effects; |
||||
|
if ((ieffects & (int) AvnDragDropEffects::Copy) != 0) |
||||
|
op |= NSDragOperationCopy; |
||||
|
if ((ieffects & (int) AvnDragDropEffects::Link) != 0) |
||||
|
op |= NSDragOperationLink; |
||||
|
if ((ieffects & (int) AvnDragDropEffects::Move) != 0) |
||||
|
op |= NSDragOperationMove; |
||||
|
[View beginDraggingSessionWithItems:@[dragItem] event:nsevent |
||||
|
source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
bool WindowBaseImpl::IsDialog() { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
NSWindowStyleMask WindowBaseImpl::GetStyle() { |
||||
|
return NSWindowStyleMaskBorderless; |
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::UpdateStyle() { |
||||
|
[Window setStyleMask:GetStyle()]; |
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::CleanNSWindow() { |
||||
|
if(Window != nullptr) { |
||||
|
[GetWindowProtocol() disconnectParent]; |
||||
|
[Window close]; |
||||
|
Window = nullptr; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::CreateNSWindow(bool isDialog) { |
||||
|
if (isDialog) { |
||||
|
if (![Window isKindOfClass:[AvnPanel class]]) { |
||||
|
CleanNSWindow(); |
||||
|
|
||||
|
Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; |
||||
|
} |
||||
|
} else { |
||||
|
if (![Window isKindOfClass:[AvnWindow class]]) { |
||||
|
CleanNSWindow(); |
||||
|
|
||||
|
Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::OnInitialiseNSWindow() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void WindowBaseImpl::InitialiseNSWindow() { |
||||
|
if(Window != nullptr) { |
||||
|
[Window setContentView:StandardContainer]; |
||||
|
[Window setStyleMask:NSWindowStyleMaskBorderless]; |
||||
|
[Window setBackingType:NSBackingStoreBuffered]; |
||||
|
|
||||
|
[Window setContentSize:lastSize]; |
||||
|
[Window setContentMinSize:lastMinSize]; |
||||
|
[Window setContentMaxSize:lastMaxSize]; |
||||
|
|
||||
|
[Window setOpaque:false]; |
||||
|
|
||||
|
[Window invalidateShadow]; |
||||
|
|
||||
|
if (lastMenu != nullptr) { |
||||
|
[GetWindowProtocol() applyMenu:lastMenu]; |
||||
|
|
||||
|
if ([Window isKeyWindow]) { |
||||
|
[GetWindowProtocol() showWindowMenuWithAppMenu]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
OnInitialiseNSWindow(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
id <AvnWindowProtocol> WindowBaseImpl::GetWindowProtocol() { |
||||
|
if(Window == nullptr) |
||||
|
{ |
||||
|
return nullptr; |
||||
|
} |
||||
|
|
||||
|
return (id <AvnWindowProtocol>) Window; |
||||
|
} |
||||
|
|
||||
|
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); |
||||
|
return ptr; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 04/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H |
||||
|
#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H |
||||
|
|
||||
|
#import "WindowBaseImpl.h" |
||||
|
#include "IWindowStateChanged.h" |
||||
|
|
||||
|
class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged |
||||
|
{ |
||||
|
private: |
||||
|
bool _canResize; |
||||
|
bool _fullScreenActive; |
||||
|
SystemDecorations _decorations; |
||||
|
AvnWindowState _lastWindowState; |
||||
|
AvnWindowState _actualWindowState; |
||||
|
bool _inSetWindowState; |
||||
|
NSRect _preZoomSize; |
||||
|
bool _transitioningWindowState; |
||||
|
bool _isClientAreaExtended; |
||||
|
bool _isDialog; |
||||
|
AvnExtendClientAreaChromeHints _extendClientHints; |
||||
|
|
||||
|
FORWARD_IUNKNOWN() |
||||
|
BEGIN_INTERFACE_MAP() |
||||
|
INHERIT_INTERFACE_MAP(WindowBaseImpl) |
||||
|
INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) |
||||
|
END_INTERFACE_MAP() |
||||
|
virtual ~WindowImpl() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
ComPtr<IAvnWindowEvents> WindowEvents; |
||||
|
|
||||
|
WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); |
||||
|
|
||||
|
void HideOrShowTrafficLights (); |
||||
|
|
||||
|
virtual HRESULT Show (bool activate, bool isDialog) override; |
||||
|
|
||||
|
virtual HRESULT SetEnabled (bool enable) override; |
||||
|
|
||||
|
virtual HRESULT SetParent (IAvnWindow* parent) override; |
||||
|
|
||||
|
void StartStateTransition () override ; |
||||
|
|
||||
|
void EndStateTransition () override ; |
||||
|
|
||||
|
SystemDecorations Decorations () override ; |
||||
|
|
||||
|
AvnWindowState WindowState () override ; |
||||
|
|
||||
|
void WindowStateChanged () override ; |
||||
|
|
||||
|
bool UndecoratedIsMaximized (); |
||||
|
|
||||
|
bool IsZoomed (); |
||||
|
|
||||
|
void DoZoom(); |
||||
|
|
||||
|
virtual HRESULT SetCanResize(bool value) override; |
||||
|
|
||||
|
virtual HRESULT SetDecorations(SystemDecorations value) override; |
||||
|
|
||||
|
virtual HRESULT SetTitle (char* utf8title) override; |
||||
|
|
||||
|
virtual HRESULT SetTitleBarColor(AvnColor color) override; |
||||
|
|
||||
|
virtual HRESULT GetWindowState (AvnWindowState*ret) override; |
||||
|
|
||||
|
virtual HRESULT TakeFocusFromChildren () override; |
||||
|
|
||||
|
virtual HRESULT SetExtendClientArea (bool enable) override; |
||||
|
|
||||
|
virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override; |
||||
|
|
||||
|
virtual HRESULT GetExtendTitleBarHeight (double*ret) override; |
||||
|
|
||||
|
virtual HRESULT SetExtendTitleBarHeight (double value) override; |
||||
|
|
||||
|
void EnterFullScreenMode (); |
||||
|
|
||||
|
void ExitFullScreenMode (); |
||||
|
|
||||
|
virtual HRESULT SetWindowState (AvnWindowState state) override; |
||||
|
|
||||
|
virtual bool IsDialog() override; |
||||
|
|
||||
|
virtual void OnInitialiseNSWindow() override; |
||||
|
|
||||
|
protected: |
||||
|
virtual NSWindowStyleMask GetStyle() override; |
||||
|
|
||||
|
private: |
||||
|
NSString *_lastTitle; |
||||
|
}; |
||||
|
|
||||
|
#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H
|
||||
@ -0,0 +1,554 @@ |
|||||
|
// |
||||
|
// Created by Dan Walmsley on 04/05/2022. |
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved. |
||||
|
// |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "AutoFitContentView.h" |
||||
|
#include "AvnView.h" |
||||
|
#include "automation.h" |
||||
|
#include "WindowProtocol.h" |
||||
|
|
||||
|
WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { |
||||
|
_isClientAreaExtended = false; |
||||
|
_extendClientHints = AvnDefaultChrome; |
||||
|
_fullScreenActive = false; |
||||
|
_canResize = true; |
||||
|
_decorations = SystemDecorationsFull; |
||||
|
_transitioningWindowState = false; |
||||
|
_inSetWindowState = false; |
||||
|
_lastWindowState = Normal; |
||||
|
_actualWindowState = Normal; |
||||
|
_lastTitle = @""; |
||||
|
WindowEvents = events; |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::HideOrShowTrafficLights() { |
||||
|
if (Window == nil) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (id subview in Window.contentView.superview.subviews) { |
||||
|
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { |
||||
|
NSView *titlebarView = [subview subviews][0]; |
||||
|
for (id button in titlebarView.subviews) { |
||||
|
if ([button isKindOfClass:[NSButton class]]) { |
||||
|
if (_isClientAreaExtended) { |
||||
|
auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); |
||||
|
|
||||
|
[button setHidden:!wantsChrome]; |
||||
|
} else { |
||||
|
[button setHidden:(_decorations != SystemDecorationsFull)]; |
||||
|
} |
||||
|
|
||||
|
[button setWantsLayer:true]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::OnInitialiseNSWindow(){ |
||||
|
[GetWindowProtocol() setCanBecomeKeyWindow:true]; |
||||
|
[Window disableCursorRects]; |
||||
|
[Window setTabbingMode:NSWindowTabbingModeDisallowed]; |
||||
|
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; |
||||
|
|
||||
|
[Window setTitle:_lastTitle]; |
||||
|
|
||||
|
if(_isClientAreaExtended) |
||||
|
{ |
||||
|
[GetWindowProtocol() setIsExtended:true]; |
||||
|
SetExtendClientArea(true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::Show(bool activate, bool isDialog) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
_isDialog = isDialog; |
||||
|
|
||||
|
WindowBaseImpl::Show(activate, isDialog); |
||||
|
|
||||
|
HideOrShowTrafficLights(); |
||||
|
|
||||
|
return SetWindowState(_lastWindowState); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetEnabled(bool enable) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
[GetWindowProtocol() setEnabled:enable]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetParent(IAvnWindow *parent) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (parent == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
auto cparent = dynamic_cast<WindowImpl *>(parent); |
||||
|
if (cparent == nullptr) |
||||
|
return E_INVALIDARG; |
||||
|
|
||||
|
// If one tries to show a child window with a minimized parent window, then the parent window will be |
||||
|
// restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive |
||||
|
// state. Detect this and explicitly restore the parent window ourselves to avoid this situation. |
||||
|
if (cparent->WindowState() == Minimized) |
||||
|
cparent->SetWindowState(Normal); |
||||
|
|
||||
|
[Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; |
||||
|
[cparent->Window addChildWindow:Window ordered:NSWindowAbove]; |
||||
|
|
||||
|
UpdateStyle(); |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::StartStateTransition() { |
||||
|
_transitioningWindowState = true; |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::EndStateTransition() { |
||||
|
_transitioningWindowState = false; |
||||
|
} |
||||
|
|
||||
|
SystemDecorations WindowImpl::Decorations() { |
||||
|
return _decorations; |
||||
|
} |
||||
|
|
||||
|
AvnWindowState WindowImpl::WindowState() { |
||||
|
return _lastWindowState; |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::WindowStateChanged() { |
||||
|
if (_shown && !_inSetWindowState && !_transitioningWindowState) { |
||||
|
AvnWindowState state; |
||||
|
GetWindowState(&state); |
||||
|
|
||||
|
if (_lastWindowState != state) { |
||||
|
if (_isClientAreaExtended) { |
||||
|
if (_lastWindowState == FullScreen) { |
||||
|
// we exited fs. |
||||
|
if (_extendClientHints & AvnOSXThickTitleBar) { |
||||
|
Window.toolbar = [NSToolbar new]; |
||||
|
Window.toolbar.showsBaselineSeparator = false; |
||||
|
} |
||||
|
|
||||
|
[Window setTitlebarAppearsTransparent:true]; |
||||
|
|
||||
|
[StandardContainer setFrameSize:StandardContainer.frame.size]; |
||||
|
} else if (state == FullScreen) { |
||||
|
// we entered fs. |
||||
|
if (_extendClientHints & AvnOSXThickTitleBar) { |
||||
|
Window.toolbar = nullptr; |
||||
|
} |
||||
|
|
||||
|
[Window setTitlebarAppearsTransparent:false]; |
||||
|
|
||||
|
[StandardContainer setFrameSize:StandardContainer.frame.size]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_lastWindowState = state; |
||||
|
_actualWindowState = state; |
||||
|
WindowEvents->WindowStateChanged(state); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool WindowImpl::UndecoratedIsMaximized() { |
||||
|
auto windowSize = [Window frame]; |
||||
|
auto available = [Window screen].visibleFrame; |
||||
|
return CGRectEqualToRect(windowSize, available); |
||||
|
} |
||||
|
|
||||
|
bool WindowImpl::IsZoomed() { |
||||
|
return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::DoZoom() { |
||||
|
switch (_decorations) { |
||||
|
case SystemDecorationsNone: |
||||
|
case SystemDecorationsBorderOnly: |
||||
|
[Window setFrame:[Window screen].visibleFrame display:true]; |
||||
|
break; |
||||
|
|
||||
|
|
||||
|
case SystemDecorationsFull: |
||||
|
[Window performZoom:Window]; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetCanResize(bool value) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
_canResize = value; |
||||
|
UpdateStyle(); |
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetDecorations(SystemDecorations value) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
auto currentWindowState = _lastWindowState; |
||||
|
_decorations = value; |
||||
|
|
||||
|
if (_fullScreenActive) { |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
UpdateStyle(); |
||||
|
|
||||
|
HideOrShowTrafficLights(); |
||||
|
|
||||
|
switch (_decorations) { |
||||
|
case SystemDecorationsNone: |
||||
|
[Window setHasShadow:NO]; |
||||
|
[Window setTitleVisibility:NSWindowTitleHidden]; |
||||
|
[Window setTitlebarAppearsTransparent:YES]; |
||||
|
|
||||
|
if (currentWindowState == Maximized) { |
||||
|
if (!UndecoratedIsMaximized()) { |
||||
|
DoZoom(); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case SystemDecorationsBorderOnly: |
||||
|
[Window setHasShadow:YES]; |
||||
|
[Window setTitleVisibility:NSWindowTitleHidden]; |
||||
|
[Window setTitlebarAppearsTransparent:YES]; |
||||
|
|
||||
|
if (currentWindowState == Maximized) { |
||||
|
if (!UndecoratedIsMaximized()) { |
||||
|
DoZoom(); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case SystemDecorationsFull: |
||||
|
[Window setHasShadow:YES]; |
||||
|
[Window setTitleVisibility:NSWindowTitleVisible]; |
||||
|
[Window setTitlebarAppearsTransparent:NO]; |
||||
|
[Window setTitle:_lastTitle]; |
||||
|
|
||||
|
if (currentWindowState == Maximized) { |
||||
|
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; |
||||
|
|
||||
|
[View setFrameSize:newFrame]; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetTitle(char *utf8title) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
_lastTitle = [NSString stringWithUTF8String:(const char *) utf8title]; |
||||
|
[Window setTitle:_lastTitle]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetTitleBarColor(AvnColor color) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
float a = (float) color.Alpha / 255.0f; |
||||
|
float r = (float) color.Red / 255.0f; |
||||
|
float g = (float) color.Green / 255.0f; |
||||
|
float b = (float) color.Blue / 255.0f; |
||||
|
|
||||
|
auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; |
||||
|
|
||||
|
// Based on the titlebar color we have to choose either light or dark |
||||
|
// OSX doesnt let you set a foreground color for titlebar. |
||||
|
if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) { |
||||
|
[Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; |
||||
|
} else { |
||||
|
[Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; |
||||
|
} |
||||
|
|
||||
|
[Window setTitlebarAppearsTransparent:true]; |
||||
|
[Window setBackgroundColor:nscolor]; |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) { |
||||
|
*ret = FullScreen; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
if ([Window isMiniaturized]) { |
||||
|
*ret = Minimized; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
if (IsZoomed()) { |
||||
|
*ret = Maximized; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
*ret = Normal; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::TakeFocusFromChildren() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (Window == nil) |
||||
|
return S_OK; |
||||
|
if ([Window isKeyWindow]) |
||||
|
[Window makeFirstResponder:View]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetExtendClientArea(bool enable) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
_isClientAreaExtended = enable; |
||||
|
|
||||
|
if(Window != nullptr) { |
||||
|
if (enable) { |
||||
|
Window.titleVisibility = NSWindowTitleHidden; |
||||
|
|
||||
|
[Window setTitlebarAppearsTransparent:true]; |
||||
|
|
||||
|
auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); |
||||
|
|
||||
|
if (wantsTitleBar) { |
||||
|
[StandardContainer ShowTitleBar:true]; |
||||
|
} else { |
||||
|
[StandardContainer ShowTitleBar:false]; |
||||
|
} |
||||
|
|
||||
|
if (_extendClientHints & AvnOSXThickTitleBar) { |
||||
|
Window.toolbar = [NSToolbar new]; |
||||
|
Window.toolbar.showsBaselineSeparator = false; |
||||
|
} else { |
||||
|
Window.toolbar = nullptr; |
||||
|
} |
||||
|
} else { |
||||
|
Window.titleVisibility = NSWindowTitleVisible; |
||||
|
Window.toolbar = nullptr; |
||||
|
[Window setTitlebarAppearsTransparent:false]; |
||||
|
View.layer.zPosition = 0; |
||||
|
} |
||||
|
|
||||
|
[GetWindowProtocol() setIsExtended:enable]; |
||||
|
|
||||
|
HideOrShowTrafficLights(); |
||||
|
|
||||
|
UpdateStyle(); |
||||
|
} |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
_extendClientHints = hints; |
||||
|
|
||||
|
SetExtendClientArea(_isClientAreaExtended); |
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = [GetWindowProtocol() getExtendedTitleBarHeight]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetExtendTitleBarHeight(double value) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
[StandardContainer SetTitleBarHeightHint:value]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::EnterFullScreenMode() { |
||||
|
_fullScreenActive = true; |
||||
|
|
||||
|
[Window setTitle:_lastTitle]; |
||||
|
[Window toggleFullScreen:nullptr]; |
||||
|
} |
||||
|
|
||||
|
void WindowImpl::ExitFullScreenMode() { |
||||
|
[Window toggleFullScreen:nullptr]; |
||||
|
|
||||
|
_fullScreenActive = false; |
||||
|
|
||||
|
SetDecorations(_decorations); |
||||
|
} |
||||
|
|
||||
|
HRESULT WindowImpl::SetWindowState(AvnWindowState state) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
auto currentState = _actualWindowState; |
||||
|
_lastWindowState = state; |
||||
|
|
||||
|
if (Window == nullptr) { |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
if (_actualWindowState == state) { |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
_inSetWindowState = true; |
||||
|
|
||||
|
if (currentState == Normal) { |
||||
|
_preZoomSize = [Window frame]; |
||||
|
} |
||||
|
|
||||
|
if (_shown) { |
||||
|
switch (state) { |
||||
|
case Maximized: |
||||
|
if (currentState == FullScreen) { |
||||
|
ExitFullScreenMode(); |
||||
|
} |
||||
|
|
||||
|
lastPositionSet.X = 0; |
||||
|
lastPositionSet.Y = 0; |
||||
|
|
||||
|
if ([Window isMiniaturized]) { |
||||
|
[Window deminiaturize:Window]; |
||||
|
} |
||||
|
|
||||
|
if (!IsZoomed()) { |
||||
|
DoZoom(); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case Minimized: |
||||
|
if (currentState == FullScreen) { |
||||
|
ExitFullScreenMode(); |
||||
|
} else { |
||||
|
[Window miniaturize:Window]; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case FullScreen: |
||||
|
if ([Window isMiniaturized]) { |
||||
|
[Window deminiaturize:Window]; |
||||
|
} |
||||
|
|
||||
|
EnterFullScreenMode(); |
||||
|
break; |
||||
|
|
||||
|
case Normal: |
||||
|
if ([Window isMiniaturized]) { |
||||
|
[Window deminiaturize:Window]; |
||||
|
} |
||||
|
|
||||
|
if (currentState == FullScreen) { |
||||
|
ExitFullScreenMode(); |
||||
|
} |
||||
|
|
||||
|
if (IsZoomed()) { |
||||
|
if (_decorations == SystemDecorationsFull) { |
||||
|
DoZoom(); |
||||
|
} else { |
||||
|
[Window setFrame:_preZoomSize display:true]; |
||||
|
auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; |
||||
|
|
||||
|
[View setFrameSize:newFrame]; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
_actualWindowState = _lastWindowState; |
||||
|
WindowEvents->WindowStateChanged(_actualWindowState); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
_inSetWindowState = false; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool WindowImpl::IsDialog() { |
||||
|
return _isDialog; |
||||
|
} |
||||
|
|
||||
|
NSWindowStyleMask WindowImpl::GetStyle() { |
||||
|
unsigned long s = this->_isDialog ? NSWindowStyleMaskDocModalWindow : NSWindowStyleMaskBorderless; |
||||
|
|
||||
|
switch (_decorations) { |
||||
|
case SystemDecorationsNone: |
||||
|
s = s | NSWindowStyleMaskFullSizeContentView; |
||||
|
break; |
||||
|
|
||||
|
case SystemDecorationsBorderOnly: |
||||
|
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; |
||||
|
break; |
||||
|
|
||||
|
case SystemDecorationsFull: |
||||
|
s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; |
||||
|
|
||||
|
if (_canResize) { |
||||
|
s = s | NSWindowStyleMaskResizable; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if ([Window parentWindow] == nullptr) { |
||||
|
s |= NSWindowStyleMaskMiniaturizable; |
||||
|
} |
||||
|
|
||||
|
if (_isClientAreaExtended) { |
||||
|
s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; |
||||
|
} |
||||
|
return s; |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 06/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#import <Foundation/Foundation.h> |
||||
|
#import <AppKit/AppKit.h> |
||||
|
#include "WindowProtocol.h" |
||||
|
#include "WindowBaseImpl.h" |
||||
|
|
||||
|
@interface AvnWindow : NSWindow <AvnWindowProtocol, NSWindowDelegate> |
||||
|
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; |
||||
|
@end |
||||
|
|
||||
|
@interface AvnPanel : NSPanel <AvnWindowProtocol, NSWindowDelegate> |
||||
|
-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; |
||||
|
@end |
||||
@ -0,0 +1,27 @@ |
|||||
|
//
|
||||
|
// Created by Dan Walmsley on 06/05/2022.
|
||||
|
// Copyright (c) 2022 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#import <AppKit/AppKit.h> |
||||
|
|
||||
|
@class AvnMenu; |
||||
|
|
||||
|
@protocol AvnWindowProtocol |
||||
|
-(void) pollModalSession: (NSModalSession _Nonnull) session; |
||||
|
-(void) restoreParentWindow; |
||||
|
-(bool) shouldTryToHandleEvents; |
||||
|
-(void) setEnabled: (bool) enable; |
||||
|
-(void) showAppMenuOnly; |
||||
|
-(void) showWindowMenuWithAppMenu; |
||||
|
-(void) applyMenu:(AvnMenu* _Nullable)menu; |
||||
|
|
||||
|
-(double) getExtendedTitleBarHeight; |
||||
|
-(void) setIsExtended:(bool)value; |
||||
|
-(void) disconnectParent; |
||||
|
-(bool) isDialog; |
||||
|
|
||||
|
-(void) setCanBecomeKeyWindow:(bool)value; |
||||
|
@end |
||||
@ -1,77 +0,0 @@ |
|||||
#ifndef window_h |
|
||||
#define window_h |
|
||||
|
|
||||
class WindowBaseImpl; |
|
||||
|
|
||||
@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination> |
|
||||
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; |
|
||||
-(NSEvent* _Nonnull) lastMouseDownEvent; |
|
||||
-(AvnPoint) translateLocalPoint:(AvnPoint)pt; |
|
||||
-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; |
|
||||
-(void) onClosed; |
|
||||
-(AvnPixelSize) getPixelSize; |
|
||||
-(AvnPlatformResizeReason) getResizeReason; |
|
||||
-(void) setResizeReason:(AvnPlatformResizeReason)reason; |
|
||||
+ (AvnPoint)toAvnPoint:(CGPoint)p; |
|
||||
@end |
|
||||
|
|
||||
@interface AutoFitContentView : NSView |
|
||||
-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; |
|
||||
-(void) ShowTitleBar: (bool) show; |
|
||||
-(void) SetTitleBarHeightHint: (double) height; |
|
||||
-(void) SetContent: (NSView* _Nonnull) content; |
|
||||
-(void) ShowBlur: (bool) show; |
|
||||
@end |
|
||||
|
|
||||
@interface AvnWindow : NSWindow <NSWindowDelegate> |
|
||||
+(void) closeAll; |
|
||||
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; |
|
||||
-(void) setCanBecomeKeyAndMain; |
|
||||
-(void) pollModalSession: (NSModalSession _Nonnull) session; |
|
||||
-(void) restoreParentWindow; |
|
||||
-(bool) shouldTryToHandleEvents; |
|
||||
-(void) setEnabled: (bool) enable; |
|
||||
-(void) showAppMenuOnly; |
|
||||
-(void) showWindowMenuWithAppMenu; |
|
||||
-(void) applyMenu:(NSMenu* _Nullable)menu; |
|
||||
-(double) getScaling; |
|
||||
-(double) getExtendedTitleBarHeight; |
|
||||
-(void) setIsExtended:(bool)value; |
|
||||
-(bool) isDialog; |
|
||||
@end |
|
||||
|
|
||||
struct INSWindowHolder |
|
||||
{ |
|
||||
virtual AvnWindow* _Nonnull GetNSWindow () = 0; |
|
||||
virtual AvnView* _Nonnull GetNSView () = 0; |
|
||||
}; |
|
||||
|
|
||||
struct IWindowStateChanged |
|
||||
{ |
|
||||
virtual void WindowStateChanged () = 0; |
|
||||
virtual void StartStateTransition () = 0; |
|
||||
virtual void EndStateTransition () = 0; |
|
||||
virtual SystemDecorations Decorations () = 0; |
|
||||
virtual AvnWindowState WindowState () = 0; |
|
||||
}; |
|
||||
|
|
||||
class ResizeScope |
|
||||
{ |
|
||||
public: |
|
||||
ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason) |
|
||||
{ |
|
||||
_view = view; |
|
||||
_restore = [view getResizeReason]; |
|
||||
[view setResizeReason:reason]; |
|
||||
} |
|
||||
|
|
||||
~ResizeScope() |
|
||||
{ |
|
||||
[_view setResizeReason:_restore]; |
|
||||
} |
|
||||
private: |
|
||||
AvnView* _Nonnull _view; |
|
||||
AvnPlatformResizeReason _restore; |
|
||||
}; |
|
||||
|
|
||||
#endif /* window_h */ |
|
||||
File diff suppressed because it is too large
@ -1,7 +0,0 @@ |
|||||
<Directives> |
|
||||
<Application> |
|
||||
<Assembly Name="ControlCatalog" Dynamic="Required All"></Assembly> |
|
||||
<Assembly Name="Avalonia.Themes.Default" Dynamic="Required All"></Assembly> |
|
||||
<Assembly Name="Avalonia.Themes.Fluent" Dynamic="Required All"></Assembly> |
|
||||
</Application> |
|
||||
</Directives> |
|
||||
@ -0,0 +1,79 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" |
||||
|
xmlns:pc="clr-namespace:Avalonia.Controls.Primitives.Converters;assembly=Avalonia.Controls.ColorPicker" |
||||
|
mc:Ignorable="d" |
||||
|
d:DesignWidth="800" |
||||
|
d:DesignHeight="450" |
||||
|
x:Class="ControlCatalog.Pages.ColorPickerPage"> |
||||
|
|
||||
|
<UserControl.Resources> |
||||
|
<pc:ThirdComponentConverter x:Key="ThirdComponent" /> |
||||
|
</UserControl.Resources> |
||||
|
|
||||
|
<Grid ColumnDefinitions="Auto,10,Auto"> |
||||
|
<Grid Grid.Column="0" |
||||
|
Grid.Row="0" |
||||
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"> |
||||
|
<ColorSpectrum x:Name="ColorSpectrum1" |
||||
|
Grid.Row="0" |
||||
|
Color="Red" |
||||
|
CornerRadius="10" |
||||
|
Height="256" |
||||
|
Width="256" /> |
||||
|
<ColorSlider Grid.Row="1" |
||||
|
Margin="0,10,0,0" |
||||
|
ColorComponent="Component1" |
||||
|
ColorModel="Hsva" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" /> |
||||
|
<ColorSlider Grid.Row="2" |
||||
|
ColorComponent="Component2" |
||||
|
ColorModel="Hsva" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" /> |
||||
|
<ColorSlider Grid.Row="3" |
||||
|
ColorComponent="Component3" |
||||
|
ColorModel="Hsva" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" /> |
||||
|
<ColorSlider Grid.Row="4" |
||||
|
ColorComponent="Alpha" |
||||
|
ColorModel="Hsva" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" /> |
||||
|
<ColorPreviewer Grid.Row="5" |
||||
|
ShowAccentColors="True" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum1}" /> |
||||
|
</Grid> |
||||
|
<Grid Grid.Column="2" |
||||
|
Grid.Row="0" |
||||
|
ColumnDefinitions="Auto,Auto,Auto" |
||||
|
RowDefinitions="Auto,Auto"> |
||||
|
<ColorSlider Grid.Column="0" |
||||
|
Grid.Row="0" |
||||
|
IsAlphaMaxForced="True" |
||||
|
IsSaturationValueMaxForced="False" |
||||
|
ColorComponent="{Binding Components, ElementName=ColorSpectrum2, Converter={StaticResource ThirdComponent}}" |
||||
|
ColorModel="Hsva" |
||||
|
Orientation="Vertical" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" /> |
||||
|
<ColorSpectrum x:Name="ColorSpectrum2" |
||||
|
Grid.Column="1" |
||||
|
Grid.Row="0" |
||||
|
Color="Green" |
||||
|
Shape="Ring" |
||||
|
Height="256" |
||||
|
Width="256" /> |
||||
|
<ColorSlider Grid.Column="2" |
||||
|
Grid.Row="0" |
||||
|
ColorComponent="Alpha" |
||||
|
ColorModel="Hsva" |
||||
|
Orientation="Vertical" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" /> |
||||
|
<ColorPreviewer Grid.Column="0" |
||||
|
Grid.ColumnSpan="3" |
||||
|
Grid.Row="1" |
||||
|
ShowAccentColors="True" |
||||
|
HsvColor="{Binding HsvColor, ElementName=ColorSpectrum2}" /> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</UserControl> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public partial class ColorPickerPage : UserControl |
||||
|
{ |
||||
|
public ColorPickerPage() |
||||
|
{ |
||||
|
InitializeComponent(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,210 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="700" |
||||
|
x:Class="RenderDemo.Pages.Transform3DPage"> |
||||
|
<UserControl.Styles> |
||||
|
<Styles> |
||||
|
<Styles.Resources> |
||||
|
<Template x:Key="TestContent"> |
||||
|
<Grid RowDefinitions="*,*" ColumnDefinitions="*,*" Margin="5"> |
||||
|
<TextBlock>I'm a text</TextBlock> |
||||
|
<Button Grid.Row="0" Grid.Column="1" Content="A Button"></Button> |
||||
|
<Slider Grid.Row="1" |
||||
|
Grid.Column="0" |
||||
|
Grid.ColumnSpan="2" |
||||
|
Value="{Binding Depth}" |
||||
|
Minimum="100" |
||||
|
Maximum="300" /> |
||||
|
</Grid> |
||||
|
</Template> |
||||
|
</Styles.Resources> |
||||
|
</Styles> |
||||
|
<Style Selector="Border.Test"> |
||||
|
<Setter Property="Width" Value="200" /> |
||||
|
<Setter Property="Height" Value="200" /> |
||||
|
<Setter Property="Child" Value="{StaticResource TestContent}" /> |
||||
|
<Setter Property="BorderThickness" Value="2" /> |
||||
|
<Setter Property="BorderBrush" Value="Black" /> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="2" /> |
||||
|
</Style> |
||||
|
<Style Selector="TextBlock, Label, Slider"> |
||||
|
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
<Setter Property="Margin" Value="10,0,10,0" /> |
||||
|
</Style> |
||||
|
<Style Selector="Border TextBlock"> |
||||
|
<Setter Property="Foreground" Value="White" /> |
||||
|
</Style> |
||||
|
<Style Selector="Border Button"> |
||||
|
<Setter Property="Background" Value="White"></Setter> |
||||
|
<Setter Property="Foreground" Value="Black" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border#B1"> |
||||
|
<Style.Animations> |
||||
|
<Animation Duration="0:0:10" |
||||
|
IterationCount="Infinite"> |
||||
|
<KeyFrame Cue="0%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="0" /> |
||||
|
<Setter Property="ZIndex" Value="4" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="25%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="90" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="100%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="360" /> |
||||
|
<Setter Property="ZIndex" Value="4" /> |
||||
|
</KeyFrame> |
||||
|
</Animation> |
||||
|
</Style.Animations> |
||||
|
</Style> |
||||
|
<Style Selector="Border#B2"> |
||||
|
<Style.Animations> |
||||
|
<Animation Duration="0:0:10" |
||||
|
IterationCount="Infinite"> |
||||
|
<KeyFrame Cue="0%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="90" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="25%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="180" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="75%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="360" /> |
||||
|
<Setter Property="ZIndex" Value="4" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="100%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="450" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
</Animation> |
||||
|
</Style.Animations> |
||||
|
</Style> |
||||
|
<Style Selector="Border#B3"> |
||||
|
<Style.Animations> |
||||
|
<Animation Duration="0:0:10" |
||||
|
IterationCount="Infinite"> |
||||
|
<KeyFrame Cue="0%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="180" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="50%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="360" /> |
||||
|
<Setter Property="ZIndex" Value="4" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="75%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="450" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="100%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="540" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
</Animation> |
||||
|
</Style.Animations> |
||||
|
</Style> |
||||
|
<Style Selector="Border#B4"> |
||||
|
<Style.Animations> |
||||
|
<Animation Duration="0:0:10" |
||||
|
IterationCount="Infinite"> |
||||
|
<KeyFrame Cue="0%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="270" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="25%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="360" /> |
||||
|
<Setter Property="ZIndex" Value="4" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="50%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="450" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
<KeyFrame Cue="100%"> |
||||
|
<Setter Property="Rotate3DTransform.AngleX" Value="630" /> |
||||
|
<Setter Property="ZIndex" Value="1" /> |
||||
|
</KeyFrame> |
||||
|
</Animation> |
||||
|
</Style.Animations> |
||||
|
</Style> |
||||
|
</UserControl.Styles> |
||||
|
|
||||
|
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="*, Auto, Auto, Auto, Auto, Auto, Auto, Auto"> |
||||
|
<Grid.Clock> |
||||
|
<Clock /> |
||||
|
</Grid.Clock> |
||||
|
<Border Name="B1" Background="DarkRed" Classes="Test"> |
||||
|
<Border.RenderTransform> |
||||
|
<Rotate3DTransform CenterZ="-100" |
||||
|
Depth="{Binding Depth}" /> |
||||
|
</Border.RenderTransform> |
||||
|
</Border> |
||||
|
<Border Name="B2" Grid.Row="0" Grid.Column="0" Classes="Test" Background="DarkGreen"> |
||||
|
<Border.RenderTransform> |
||||
|
<Rotate3DTransform CenterZ="-100" |
||||
|
Depth="{Binding Depth}" /> |
||||
|
</Border.RenderTransform> |
||||
|
</Border> |
||||
|
<Border Name="B3" Grid.Row="0" Grid.Column="0" Classes="Test" Background="DarkBlue"> |
||||
|
<Border.RenderTransform> |
||||
|
<Rotate3DTransform CenterZ="-100" |
||||
|
Depth="{Binding Depth}" /> |
||||
|
</Border.RenderTransform> |
||||
|
</Border> |
||||
|
<Border Name="B4" Grid.Row="0" Grid.Column="0" Classes="Test" Background="Orange"> |
||||
|
<Border.RenderTransform> |
||||
|
<Rotate3DTransform CenterZ="-100" |
||||
|
Depth="{Binding Depth}" /> |
||||
|
</Border.RenderTransform> |
||||
|
</Border> |
||||
|
|
||||
|
<Label Grid.Column="0" Grid.Row="1">Depth: </Label> |
||||
|
<Slider Grid.Column="1" Grid.Row="1" Value="{Binding Depth}" Minimum="100" Maximum="300" /> |
||||
|
|
||||
|
<Border Grid.Row="0" Grid.Column="2" Classes="Test" ZIndex="-2"> |
||||
|
<Border.Background> |
||||
|
<LinearGradientBrush StartPoint="0%,0%" EndPoint="0%,100%"> |
||||
|
<GradientStop Offset="0" Color="Red" /> |
||||
|
<GradientStop Offset="1" Color="Blue" /> |
||||
|
</LinearGradientBrush> |
||||
|
</Border.Background> |
||||
|
<Border.Styles> |
||||
|
<Style Selector="Label"> |
||||
|
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
</Style> |
||||
|
<Style Selector="Slider"> |
||||
|
<Setter Property="Width" Value="100" /> |
||||
|
</Style> |
||||
|
</Border.Styles> |
||||
|
<Border.RenderTransform> |
||||
|
<Rotate3DTransform Depth="{Binding Depth}" |
||||
|
CenterX="{Binding CenterX}" |
||||
|
CenterY="{Binding CenterY}" |
||||
|
CenterZ="{Binding CenterZ}" |
||||
|
AngleX="{Binding AngleX}" |
||||
|
AngleY="{Binding AngleY}" |
||||
|
AngleZ="{Binding AngleZ}" /> |
||||
|
</Border.RenderTransform> |
||||
|
</Border> |
||||
|
|
||||
|
<Label Grid.Row="1" Grid.Column="2">Center X: </Label> |
||||
|
<Slider Grid.Row="1" Grid.Column="3" Value="{Binding CenterX}" Minimum="-100" Maximum="100" /> |
||||
|
|
||||
|
<Label Grid.Row="2" Grid.Column="2">Center Y: </Label> |
||||
|
<Slider Grid.Row="2" Grid.Column="3" Value="{Binding CenterY}" Minimum="-100" Maximum="100" /> |
||||
|
|
||||
|
<Label Grid.Row="3" Grid.Column="2">Center Z: </Label> |
||||
|
<Slider Grid.Row="3" Grid.Column="3" Value="{Binding CenterZ}" Minimum="-100" Maximum="100" /> |
||||
|
|
||||
|
<Label Grid.Row="4" Grid.Column="2">Angle X: </Label> |
||||
|
<Slider Grid.Row="4" Grid.Column="3" Value="{Binding AngleX}" Minimum="-180" Maximum="180" /> |
||||
|
|
||||
|
<Label Grid.Row="5" Grid.Column="2">Angle Y: </Label> |
||||
|
<Slider Grid.Row="5" Grid.Column="3" Value="{Binding AngleY}" Minimum="-180" Maximum="180" /> |
||||
|
|
||||
|
<Label Grid.Row="6" Grid.Column="2">Angle Z: </Label> |
||||
|
<Slider Grid.Row="6" Grid.Column="3" Value="{Binding AngleZ}" Minimum="-180" Maximum="180" /> |
||||
|
</Grid> |
||||
|
</UserControl> |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using RenderDemo.ViewModels; |
||||
|
|
||||
|
namespace RenderDemo.Pages; |
||||
|
|
||||
|
public class Transform3DPage : UserControl |
||||
|
{ |
||||
|
public Transform3DPage() |
||||
|
{ |
||||
|
InitializeComponent(); |
||||
|
this.DataContext = new Transform3DPageViewModel(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,55 @@ |
|||||
|
using System; |
||||
|
using MiniMvvm; |
||||
|
using Avalonia.Animation; |
||||
|
|
||||
|
namespace RenderDemo.ViewModels |
||||
|
{ |
||||
|
public class Transform3DPageViewModel : ViewModelBase |
||||
|
{ |
||||
|
private double _depth = 200; |
||||
|
|
||||
|
private double _centerX = 0; |
||||
|
private double _centerY = 0; |
||||
|
private double _centerZ = 0; |
||||
|
private double _angleX = 0; |
||||
|
private double _angleY = 0; |
||||
|
private double _angleZ = 0; |
||||
|
|
||||
|
public double Depth |
||||
|
{ |
||||
|
get => _depth; |
||||
|
set => RaiseAndSetIfChanged(ref _depth, value); |
||||
|
} |
||||
|
|
||||
|
public double CenterX |
||||
|
{ |
||||
|
get => _centerX; |
||||
|
set => RaiseAndSetIfChanged(ref _centerX, value); |
||||
|
} |
||||
|
public double CenterY |
||||
|
{ |
||||
|
get => _centerY; |
||||
|
set => RaiseAndSetIfChanged(ref _centerY, value); |
||||
|
} |
||||
|
public double CenterZ |
||||
|
{ |
||||
|
get => _centerZ; |
||||
|
set => RaiseAndSetIfChanged(ref _centerZ, value); |
||||
|
} |
||||
|
public double AngleX |
||||
|
{ |
||||
|
get => _angleX; |
||||
|
set => RaiseAndSetIfChanged(ref _angleX, value); |
||||
|
} |
||||
|
public double AngleY |
||||
|
{ |
||||
|
get => _angleY; |
||||
|
set => RaiseAndSetIfChanged(ref _angleY, value); |
||||
|
} |
||||
|
public double AngleZ |
||||
|
{ |
||||
|
get => _angleZ; |
||||
|
set => RaiseAndSetIfChanged(ref _angleZ, value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,43 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
<PropertyGroup> |
|
||||
<TargetFramework>net6.0-android</TargetFramework> |
|
||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> |
|
||||
<OutputType>Exe</OutputType> |
|
||||
<Nullable>enable</Nullable> |
|
||||
<ApplicationId>com.Avalonia.AndroidTestApplication</ApplicationId> |
|
||||
<ApplicationVersion>1</ApplicationVersion> |
|
||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> |
|
||||
<AndroidPackageFormat>apk</AndroidPackageFormat> |
|
||||
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> |
|
||||
<DebugType>portable</DebugType> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<AndroidResource Include="..\..\..\build\Assets\Icon.png"> |
|
||||
<Link>Resources\drawable\Icon.png</Link> |
|
||||
</AndroidResource> |
|
||||
</ItemGroup> |
|
||||
|
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''"> |
|
||||
<DebugSymbols>True</DebugSymbols> |
|
||||
<RunAOTCompilation>True</RunAOTCompilation> |
|
||||
<EnableLLVM>True</EnableLLVM> |
|
||||
<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<None Remove="Assets\AboutAssets.txt" /> |
|
||||
</ItemGroup> |
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
|
||||
<BundleAssemblies>True</BundleAssemblies> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> |
|
||||
<BundleAssemblies>True</BundleAssemblies> |
|
||||
</PropertyGroup> |
|
||||
|
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> |
|
||||
<ProjectReference Include="..\Avalonia.Android\Avalonia.Android.csproj" /> |
|
||||
</ItemGroup> |
|
||||
</Project> |
|
||||
@ -1,104 +0,0 @@ |
|||||
using System; |
|
||||
using Android.App; |
|
||||
using Android.Content.PM; |
|
||||
using Avalonia.Android; |
|
||||
using Avalonia.Controls; |
|
||||
using Avalonia.Controls.ApplicationLifetimes; |
|
||||
using Avalonia.Input.TextInput; |
|
||||
using Avalonia.Markup.Xaml; |
|
||||
using Avalonia.Media; |
|
||||
using Avalonia.Styling; |
|
||||
using Avalonia.Themes.Default; |
|
||||
|
|
||||
namespace Avalonia.AndroidTestApplication |
|
||||
{ |
|
||||
[Activity(Label = "Main", |
|
||||
MainLauncher = true, |
|
||||
Icon = "@drawable/icon", |
|
||||
Theme = "@style/Theme.AppCompat.NoActionBar", |
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, |
|
||||
LaunchMode = LaunchMode.SingleInstance/*, |
|
||||
ScreenOrientation = ScreenOrientation.Landscape*/)] |
|
||||
public class MainActivity : AvaloniaActivity<App> |
|
||||
{ |
|
||||
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) |
|
||||
{ |
|
||||
return base.CustomizeAppBuilder(builder); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public class App : Application |
|
||||
{ |
|
||||
public override void Initialize() |
|
||||
{ |
|
||||
Styles.Add(new SimpleTheme(new Uri("avares://Avalonia.AndroidTestApplication"))); |
|
||||
} |
|
||||
|
|
||||
public override void OnFrameworkInitializationCompleted() |
|
||||
{ |
|
||||
if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) |
|
||||
{ |
|
||||
singleViewLifetime.MainView = CreateSimpleWindow(); |
|
||||
} |
|
||||
|
|
||||
base.OnFrameworkInitializationCompleted(); |
|
||||
} |
|
||||
|
|
||||
// This provides a simple UI tree for testing input handling, drawing, etc
|
|
||||
public static ContentControl CreateSimpleWindow() |
|
||||
{ |
|
||||
ContentControl window = new ContentControl() |
|
||||
{ |
|
||||
Background = Brushes.Red, |
|
||||
Content = new StackPanel |
|
||||
{ |
|
||||
Margin = new Thickness(30), |
|
||||
Background = Brushes.Yellow, |
|
||||
Children = |
|
||||
{ |
|
||||
new TextBlock |
|
||||
{ |
|
||||
Text = "TEXT BLOCK", |
|
||||
Width = 300, |
|
||||
Height = 40, |
|
||||
Background = Brushes.White, |
|
||||
Foreground = Brushes.Black |
|
||||
}, |
|
||||
|
|
||||
new Button |
|
||||
{ |
|
||||
Content = "BUTTON", |
|
||||
Width = 150, |
|
||||
Height = 40, |
|
||||
Background = Brushes.LightGreen, |
|
||||
Foreground = Brushes.Black |
|
||||
}, |
|
||||
|
|
||||
CreateTextBox(TextInputContentType.Normal), |
|
||||
CreateTextBox(TextInputContentType.Password), |
|
||||
CreateTextBox(TextInputContentType.Email), |
|
||||
CreateTextBox(TextInputContentType.Url), |
|
||||
CreateTextBox(TextInputContentType.Digits), |
|
||||
CreateTextBox(TextInputContentType.Number), |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
return window; |
|
||||
} |
|
||||
|
|
||||
private static TextBox CreateTextBox(TextInputContentType contentType) |
|
||||
{ |
|
||||
var textBox = new TextBox() |
|
||||
{ |
|
||||
Margin = new Thickness(20, 10), |
|
||||
Watermark = contentType.ToString(), |
|
||||
BorderThickness = new Thickness(3), |
|
||||
FontSize = 20, |
|
||||
[TextInputOptions.ContentTypeProperty] = contentType |
|
||||
}; |
|
||||
|
|
||||
return textBox; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,4 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
|
||||
<application android:label="Avalonia.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application> |
|
||||
</manifest> |
|
||||
@ -1,50 +0,0 @@ |
|||||
Images, layout descriptions, binary blobs and string dictionaries can be included |
|
||||
in your application as resource files. Various Android APIs are designed to |
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs |
|
||||
directly. |
|
||||
|
|
||||
For example, a sample Android app that contains a user interface layout (main.xml), |
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) |
|
||||
would keep its resources in the "Resources" directory of the application: |
|
||||
|
|
||||
Resources/ |
|
||||
drawable-hdpi/ |
|
||||
icon.png |
|
||||
|
|
||||
drawable-ldpi/ |
|
||||
icon.png |
|
||||
|
|
||||
drawable-mdpi/ |
|
||||
icon.png |
|
||||
|
|
||||
layout/ |
|
||||
main.xml |
|
||||
|
|
||||
values/ |
|
||||
strings.xml |
|
||||
|
|
||||
In order to get the build system to recognize Android resources, set the build action to |
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but |
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources, |
|
||||
the build system will package the resources for distribution and generate a class called |
|
||||
"Resource" that contains the tokens for each one of the resources included. For example, |
|
||||
for the above Resources layout, this is what the Resource class would expose: |
|
||||
|
|
||||
public class Resource { |
|
||||
public class drawable { |
|
||||
public const int icon = 0x123; |
|
||||
} |
|
||||
|
|
||||
public class layout { |
|
||||
public const int main = 0x456; |
|
||||
} |
|
||||
|
|
||||
public class strings { |
|
||||
public const int first_string = 0xabc; |
|
||||
public const int second_string = 0xbcd; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main |
|
||||
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first |
|
||||
string in the dictionary file values/strings.xml. |
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,6 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
|
|
||||
<resources> |
|
||||
<string name="Hello">Hello World, Click Me!</string> |
|
||||
<string name="ApplicationName">Avalonia.AndroidTestApplication</string> |
|
||||
</resources> |
|
||||
@ -1,253 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Collections.Specialized; |
|
||||
using System.Linq; |
|
||||
using Avalonia.Data; |
|
||||
|
|
||||
#nullable enable |
|
||||
|
|
||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Base class for all animatable objects.
|
|
||||
/// </summary>
|
|
||||
public class Animatable : AvaloniaObject |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Defines the <see cref="Clock"/> property.
|
|
||||
/// </summary>
|
|
||||
public static readonly StyledProperty<IClock> ClockProperty = |
|
||||
AvaloniaProperty.Register<Animatable, IClock>(nameof(Clock), inherits: true); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Defines the <see cref="Transitions"/> property.
|
|
||||
/// </summary>
|
|
||||
public static readonly StyledProperty<Transitions?> TransitionsProperty = |
|
||||
AvaloniaProperty.Register<Animatable, Transitions?>(nameof(Transitions)); |
|
||||
|
|
||||
private bool _transitionsEnabled = true; |
|
||||
private Dictionary<ITransition, TransitionState>? _transitionState; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the clock which controls the animations on the control.
|
|
||||
/// </summary>
|
|
||||
public IClock Clock |
|
||||
{ |
|
||||
get => GetValue(ClockProperty); |
|
||||
set => SetValue(ClockProperty, value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets or sets the property transitions for the control.
|
|
||||
/// </summary>
|
|
||||
public Transitions? Transitions |
|
||||
{ |
|
||||
get => GetValue(TransitionsProperty); |
|
||||
set => SetValue(TransitionsProperty, value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Enables transitions for the control.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// This method should not be called from user code, it will be called automatically by the framework
|
|
||||
/// when a control is added to the visual tree.
|
|
||||
/// </remarks>
|
|
||||
protected void EnableTransitions() |
|
||||
{ |
|
||||
if (!_transitionsEnabled) |
|
||||
{ |
|
||||
_transitionsEnabled = true; |
|
||||
|
|
||||
if (Transitions is object) |
|
||||
{ |
|
||||
AddTransitions(Transitions); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Disables transitions for the control.
|
|
||||
/// </summary>
|
|
||||
/// <remarks>
|
|
||||
/// This method should not be called from user code, it will be called automatically by the framework
|
|
||||
/// when a control is added to the visual tree.
|
|
||||
/// </remarks>
|
|
||||
protected void DisableTransitions() |
|
||||
{ |
|
||||
if (_transitionsEnabled) |
|
||||
{ |
|
||||
_transitionsEnabled = false; |
|
||||
|
|
||||
if (Transitions is object) |
|
||||
{ |
|
||||
RemoveTransitions(Transitions); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
protected sealed override void OnPropertyChangedCore<T>(AvaloniaPropertyChangedEventArgs<T> change) |
|
||||
{ |
|
||||
if (change.Property == TransitionsProperty && change.IsEffectiveValueChange) |
|
||||
{ |
|
||||
var oldTransitions = change.OldValue.GetValueOrDefault<Transitions>(); |
|
||||
var newTransitions = change.NewValue.GetValueOrDefault<Transitions>(); |
|
||||
|
|
||||
// When transitions are replaced, we add the new transitions before removing the old
|
|
||||
// transitions, so that when the old transition being disposed causes the value to
|
|
||||
// change, there is a corresponding entry in `_transitionStates`. This means that we
|
|
||||
// need to account for any transitions present in both the old and new transitions
|
|
||||
// collections.
|
|
||||
if (newTransitions is object) |
|
||||
{ |
|
||||
var toAdd = (IList)newTransitions; |
|
||||
|
|
||||
if (newTransitions.Count > 0 && oldTransitions?.Count > 0) |
|
||||
{ |
|
||||
toAdd = newTransitions.Except(oldTransitions).ToList(); |
|
||||
} |
|
||||
|
|
||||
newTransitions.CollectionChanged += TransitionsCollectionChanged; |
|
||||
AddTransitions(toAdd); |
|
||||
} |
|
||||
|
|
||||
if (oldTransitions is object) |
|
||||
{ |
|
||||
var toRemove = (IList)oldTransitions; |
|
||||
|
|
||||
if (oldTransitions.Count > 0 && newTransitions?.Count > 0) |
|
||||
{ |
|
||||
toRemove = oldTransitions.Except(newTransitions).ToList(); |
|
||||
} |
|
||||
|
|
||||
oldTransitions.CollectionChanged -= TransitionsCollectionChanged; |
|
||||
RemoveTransitions(toRemove); |
|
||||
} |
|
||||
} |
|
||||
else if (_transitionsEnabled && |
|
||||
Transitions is object && |
|
||||
_transitionState is object && |
|
||||
!change.Property.IsDirect && |
|
||||
change.Priority > BindingPriority.Animation) |
|
||||
{ |
|
||||
for (var i = Transitions.Count -1; i >= 0; --i) |
|
||||
{ |
|
||||
var transition = Transitions[i]; |
|
||||
|
|
||||
if (transition.Property == change.Property && |
|
||||
_transitionState.TryGetValue(transition, out var state)) |
|
||||
{ |
|
||||
var oldValue = state.BaseValue; |
|
||||
var newValue = GetAnimationBaseValue(transition.Property); |
|
||||
|
|
||||
if (!Equals(oldValue, newValue)) |
|
||||
{ |
|
||||
state.BaseValue = newValue; |
|
||||
|
|
||||
// We need to transition from the current animated value if present,
|
|
||||
// instead of the old base value.
|
|
||||
var animatedValue = GetValue(transition.Property); |
|
||||
|
|
||||
if (!Equals(newValue, animatedValue)) |
|
||||
{ |
|
||||
oldValue = animatedValue; |
|
||||
} |
|
||||
|
|
||||
state.Instance?.Dispose(); |
|
||||
state.Instance = transition.Apply( |
|
||||
this, |
|
||||
Clock ?? AvaloniaLocator.Current.GetRequiredService<IGlobalClock>(), |
|
||||
oldValue, |
|
||||
newValue); |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
base.OnPropertyChangedCore(change); |
|
||||
} |
|
||||
|
|
||||
private void TransitionsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) |
|
||||
{ |
|
||||
if (!_transitionsEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
switch (e.Action) |
|
||||
{ |
|
||||
case NotifyCollectionChangedAction.Add: |
|
||||
AddTransitions(e.NewItems!); |
|
||||
break; |
|
||||
case NotifyCollectionChangedAction.Remove: |
|
||||
RemoveTransitions(e.OldItems!); |
|
||||
break; |
|
||||
case NotifyCollectionChangedAction.Replace: |
|
||||
RemoveTransitions(e.OldItems!); |
|
||||
AddTransitions(e.NewItems!); |
|
||||
break; |
|
||||
case NotifyCollectionChangedAction.Reset: |
|
||||
throw new NotSupportedException("Transitions collection cannot be reset."); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void AddTransitions(IList items) |
|
||||
{ |
|
||||
if (!_transitionsEnabled) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
_transitionState ??= new Dictionary<ITransition, TransitionState>(); |
|
||||
|
|
||||
for (var i = 0; i < items.Count; ++i) |
|
||||
{ |
|
||||
var t = (ITransition)items[i]!; |
|
||||
|
|
||||
_transitionState.Add(t, new TransitionState |
|
||||
{ |
|
||||
BaseValue = GetAnimationBaseValue(t.Property), |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void RemoveTransitions(IList items) |
|
||||
{ |
|
||||
if (_transitionState is null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
for (var i = 0; i < items.Count; ++i) |
|
||||
{ |
|
||||
var t = (ITransition)items[i]!; |
|
||||
|
|
||||
if (_transitionState.TryGetValue(t, out var state)) |
|
||||
{ |
|
||||
state.Instance?.Dispose(); |
|
||||
_transitionState.Remove(t); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private object? GetAnimationBaseValue(AvaloniaProperty property) |
|
||||
{ |
|
||||
var value = this.GetBaseValue(property, BindingPriority.LocalValue); |
|
||||
|
|
||||
if (value == AvaloniaProperty.UnsetValue) |
|
||||
{ |
|
||||
value = GetValue(property); |
|
||||
} |
|
||||
|
|
||||
return value; |
|
||||
} |
|
||||
|
|
||||
private class TransitionState |
|
||||
{ |
|
||||
public IDisposable? Instance { get; set; } |
|
||||
public object? BaseValue { get; set; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,5 +0,0 @@ |
|||||
Compat issues with assembly Avalonia.Animation: |
|
||||
InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' is present in the contract but not in the implementation. |
|
||||
MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' does not exist in the implementation but it does exist in the contract. |
|
||||
InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock, System.Threading.CancellationToken)' is present in the implementation but not in the contract. |
|
||||
Total Issues: 3 |
|
||||
@ -1,15 +0,0 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
|
||||
<PropertyGroup> |
|
||||
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks> |
|
||||
</PropertyGroup> |
|
||||
<ItemGroup> |
|
||||
<Compile Include="..\Avalonia.Base\Metadata\NullableAttributes.cs" Link="NullableAttributes.cs" /> |
|
||||
</ItemGroup> |
|
||||
<ItemGroup> |
|
||||
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
|
||||
</ItemGroup> |
|
||||
<Import Project="..\..\build\Rx.props" /> |
|
||||
<Import Project="..\..\build\ApiDiff.props" /> |
|
||||
<Import Project="..\..\build\NullableEnable.props" /> |
|
||||
<Import Project="..\..\build\DevAnalyzers.props" /> |
|
||||
</Project> |
|
||||
@ -1,59 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.ComponentModel; |
|
||||
using System.Globalization; |
|
||||
using System.Linq; |
|
||||
|
|
||||
namespace Avalonia.Animation.Easings |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Base class for all Easing classes.
|
|
||||
/// </summary>
|
|
||||
[TypeConverter(typeof(EasingTypeConverter))] |
|
||||
public abstract class Easing : IEasing |
|
||||
{ |
|
||||
/// <inheritdoc/>
|
|
||||
public abstract double Ease(double progress); |
|
||||
|
|
||||
static Dictionary<string, Type>? _easingTypes; |
|
||||
|
|
||||
static readonly Type s_thisType = typeof(Easing); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Parses a Easing type string.
|
|
||||
/// </summary>
|
|
||||
/// <param name="e">The Easing type string.</param>
|
|
||||
/// <returns>Returns the instance of the parsed type.</returns>
|
|
||||
public static Easing Parse(string e) |
|
||||
{ |
|
||||
if (e.Contains(',')) |
|
||||
{ |
|
||||
return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture)); |
|
||||
} |
|
||||
|
|
||||
if (_easingTypes == null) |
|
||||
{ |
|
||||
_easingTypes = new Dictionary<string, Type>(); |
|
||||
|
|
||||
// Fetch the built-in easings.
|
|
||||
var derivedTypes = typeof(Easing).Assembly.GetTypes() |
|
||||
.Where(p => p.Namespace == s_thisType.Namespace) |
|
||||
.Where(p => p.IsSubclassOf(s_thisType)) |
|
||||
.Select(p => p); |
|
||||
|
|
||||
foreach (var easingType in derivedTypes) |
|
||||
_easingTypes.Add(easingType.Name, easingType); |
|
||||
} |
|
||||
|
|
||||
if (_easingTypes.ContainsKey(e)) |
|
||||
{ |
|
||||
var type = _easingTypes[e]; |
|
||||
return (Easing)Activator.CreateInstance(type)!; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace."); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Interface for Animation objects
|
|
||||
/// </summary>
|
|
||||
public interface IAnimation |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Apply the animation to the specified control and run it when <paramref name="match" /> produces <c>true</c>.
|
|
||||
/// </summary>
|
|
||||
IDisposable Apply(Animatable control, IClock? clock, IObservable<bool> match, Action? onComplete = null); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Run the animation on the specified control.
|
|
||||
/// </summary>
|
|
||||
Task RunAsync(Animatable control, IClock clock, CancellationToken cancellationToken = default); |
|
||||
} |
|
||||
} |
|
||||
@ -1,8 +0,0 @@ |
|||||
namespace Avalonia.Animation |
|
||||
{ |
|
||||
public interface IAnimationSetter |
|
||||
{ |
|
||||
AvaloniaProperty? Property { get; set; } |
|
||||
object? Value { get; set; } |
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue