Browse Source
* Introduce Avalonia.Native TopLevelImpl * Update Avalonia.Native.csproj Revert changepull/16061/head
committed by
GitHub
60 changed files with 1378 additions and 995 deletions
@ -1,9 +0,0 @@ |
|||||
//
|
|
||||
// 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,76 @@ |
|||||
|
//
|
||||
|
// TopLevelImpl.h
|
||||
|
// Avalonia.Native.OSX
|
||||
|
//
|
||||
|
// Created by Benedikt Stebner on 16.05.24.
|
||||
|
// Copyright © 2024 Avalonia. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef TopLevelImpl_h |
||||
|
#define TopLevelImpl_h |
||||
|
|
||||
|
#include "rendertarget.h" |
||||
|
#include "INSWindowHolder.h" |
||||
|
#include "AvnTextInputMethod.h" |
||||
|
#include "AutoFitContentView.h" |
||||
|
#include <list> |
||||
|
|
||||
|
class TopLevelImpl : public virtual ComObject, |
||||
|
public virtual IAvnTopLevel, |
||||
|
public INSViewHolder{ |
||||
|
|
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
BEGIN_INTERFACE_MAP() |
||||
|
INTERFACE_MAP_ENTRY(IAvnTopLevel, IID_IAvnTopLevel) |
||||
|
END_INTERFACE_MAP() |
||||
|
|
||||
|
virtual ~TopLevelImpl(); |
||||
|
|
||||
|
TopLevelImpl(IAvnTopLevelEvents* events); |
||||
|
|
||||
|
virtual AvnView *GetNSView() override; |
||||
|
|
||||
|
virtual HRESULT SetCursor(IAvnCursor* cursor) override; |
||||
|
|
||||
|
virtual HRESULT GetScaling(double*ret) override; |
||||
|
|
||||
|
virtual HRESULT GetClientSize(AvnSize *ret) override; |
||||
|
|
||||
|
virtual HRESULT GetInputMethod(IAvnTextInputMethod **ppv) override; |
||||
|
|
||||
|
virtual HRESULT ObtainNSViewHandle(void** retOut) override; |
||||
|
|
||||
|
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) override; |
||||
|
|
||||
|
virtual HRESULT CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget** ret) override; |
||||
|
|
||||
|
virtual HRESULT CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget** ret) override; |
||||
|
|
||||
|
virtual HRESULT CreateGlRenderTarget(IAvnGlContext* context, IAvnGlSurfaceRenderTarget** ret) override; |
||||
|
|
||||
|
virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; |
||||
|
|
||||
|
virtual HRESULT Invalidate() override; |
||||
|
|
||||
|
virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; |
||||
|
|
||||
|
virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; |
||||
|
|
||||
|
virtual HRESULT SetTransparencyMode(AvnWindowTransparencyMode mode) override; |
||||
|
|
||||
|
protected: |
||||
|
NSCursor *cursor; |
||||
|
virtual void UpdateAppearance(); |
||||
|
|
||||
|
public: |
||||
|
NSObject<IRenderTarget> *currentRenderTarget; |
||||
|
ComPtr<AvnTextInputMethod> InputMethod; |
||||
|
ComPtr<IAvnTopLevelEvents> TopLevelEvents; |
||||
|
AvnView *View; |
||||
|
|
||||
|
void UpdateCursor(); |
||||
|
virtual void SetClientSize(NSSize size); |
||||
|
}; |
||||
|
|
||||
|
#endif /* TopLevelImpl_h */ |
||||
@ -0,0 +1,251 @@ |
|||||
|
#import <AppKit/AppKit.h> |
||||
|
#import <Cocoa/Cocoa.h> |
||||
|
#include "automation.h" |
||||
|
#include "cursor.h" |
||||
|
#include "AutoFitContentView.h" |
||||
|
#include "TopLevelImpl.h" |
||||
|
#include "AvnTextInputMethod.h" |
||||
|
#include "AvnView.h" |
||||
|
|
||||
|
TopLevelImpl::~TopLevelImpl() { |
||||
|
View = nullptr; |
||||
|
} |
||||
|
|
||||
|
TopLevelImpl::TopLevelImpl(IAvnTopLevelEvents *events) { |
||||
|
TopLevelEvents = events; |
||||
|
|
||||
|
View = [[AvnView alloc] initWithParent:this]; |
||||
|
InputMethod = new AvnTextInputMethod(View); |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::GetScaling(double *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
if ([View window] == nullptr) { |
||||
|
*ret = 1; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
*ret = [[View window] backingScaleFactor]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::GetClientSize(AvnSize *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) |
||||
|
return E_POINTER; |
||||
|
|
||||
|
NSRect frame = [View frame]; |
||||
|
|
||||
|
ret->Width = frame.size.width; |
||||
|
ret->Height = frame.size.height; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::GetInputMethod(IAvnTextInputMethod **retOut) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
*retOut = InputMethod; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::ObtainNSViewHandle(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge void *) View; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::ObtainNSViewHandleRetained(void **ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
*ret = (__bridge_retained void *) View; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::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 TopLevelImpl::UpdateCursor() { |
||||
|
if (cursor != nil) { |
||||
|
[cursor set]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::CreateSoftwareRenderTarget(IAvnSoftwareRenderTarget **ppv) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if(![NSThread isMainThread]) |
||||
|
return COR_E_INVALIDOPERATION; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
|
||||
|
auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: nil]; |
||||
|
*ppv = [target createSoftwareRenderTarget]; |
||||
|
[View setRenderTarget: target]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::CreateGlRenderTarget(IAvnGlContext* glContext, IAvnGlSurfaceRenderTarget **ppv) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if(![NSThread isMainThread]) |
||||
|
return COR_E_INVALIDOPERATION; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
|
||||
|
auto target = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: glContext]; |
||||
|
*ppv = [target createSurfaceRenderTarget]; |
||||
|
[View setRenderTarget: target]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::CreateMetalRenderTarget(IAvnMetalDevice* device, IAvnMetalRenderTarget **ppv) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if(![NSThread isMainThread]) |
||||
|
return COR_E_INVALIDOPERATION; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
|
||||
|
auto target = [[MetalRenderTarget alloc] initWithDevice: device]; |
||||
|
[View setRenderTarget: target]; |
||||
|
[target getRenderTarget: ppv]; |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
if (View == NULL) |
||||
|
return E_FAIL; |
||||
|
*retOut = ::CreateNativeControlHost(View); |
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
AvnView *TopLevelImpl::GetNSView() { |
||||
|
return View; |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::Invalidate() { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
[View setNeedsDisplayInRect:[View frame]]; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
HRESULT TopLevelImpl::PointToClient(AvnPoint point, AvnPoint *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
auto window = [View window]; |
||||
|
|
||||
|
if(window == nullptr){ |
||||
|
ret = &point; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
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 TopLevelImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
@autoreleasepool { |
||||
|
if (ret == nullptr) { |
||||
|
return E_POINTER; |
||||
|
} |
||||
|
|
||||
|
auto window = [View window]; |
||||
|
|
||||
|
if(window == nullptr){ |
||||
|
ret = &point; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
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 TopLevelImpl::SetTransparencyMode(AvnWindowTransparencyMode mode) { |
||||
|
START_COM_CALL; |
||||
|
|
||||
|
return S_OK; |
||||
|
} |
||||
|
|
||||
|
void TopLevelImpl::UpdateAppearance() { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
void TopLevelImpl::SetClientSize(NSSize size){ |
||||
|
[View setFrameSize:size]; |
||||
|
} |
||||
|
|
||||
|
extern IAvnTopLevel* CreateAvnTopLevel(IAvnTopLevelEvents* events) |
||||
|
{ |
||||
|
@autoreleasepool |
||||
|
{ |
||||
|
IAvnTopLevel* ptr = (IAvnTopLevel*)new TopLevelImpl(events); |
||||
|
return ptr; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Avalonia.Native.Interop; |
||||
|
|
||||
|
namespace Avalonia.Native |
||||
|
{ |
||||
|
internal class EmbeddableTopLevelImpl : TopLevelImpl |
||||
|
{ |
||||
|
public EmbeddableTopLevelImpl(IAvaloniaNativeFactory factory) : base(factory) |
||||
|
{ |
||||
|
using (var e = new TopLevelEvents(this)) |
||||
|
{ |
||||
|
Init(new MacOSTopLevelHandle(factory.CreateTopLevel(e)), factory.CreateScreens()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,557 @@ |
|||||
|
#nullable enable |
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Automation.Peers; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Controls.Platform; |
||||
|
using Avalonia.Controls.Platform.Surfaces; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Input.Platform; |
||||
|
using Avalonia.Input.Raw; |
||||
|
using Avalonia.Input.TextInput; |
||||
|
using Avalonia.Native.Interop; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Platform.Storage; |
||||
|
using Avalonia.Platform.Storage.FileIO; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
using Avalonia.Threading; |
||||
|
|
||||
|
namespace Avalonia.Native; |
||||
|
|
||||
|
internal class MacOSTopLevelHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle |
||||
|
{ |
||||
|
internal MacOSTopLevelHandle(IAvnTopLevel native) |
||||
|
{ |
||||
|
Native = native; |
||||
|
|
||||
|
HandleDescriptor = "NSView"; |
||||
|
|
||||
|
Handle = NSView; |
||||
|
} |
||||
|
|
||||
|
internal MacOSTopLevelHandle(IAvnWindowBase native) |
||||
|
{ |
||||
|
Native = native; |
||||
|
|
||||
|
HandleDescriptor = "NSWindow"; |
||||
|
|
||||
|
Handle = NSWindow; |
||||
|
} |
||||
|
|
||||
|
internal IAvnTopLevel Native { get; } |
||||
|
|
||||
|
public IntPtr Handle { get; } |
||||
|
|
||||
|
public string HandleDescriptor { get; } |
||||
|
|
||||
|
public IntPtr NSView => Native.ObtainNSViewHandle(); |
||||
|
|
||||
|
public IntPtr GetNSViewRetained() |
||||
|
{ |
||||
|
return Native.ObtainNSViewHandleRetained(); |
||||
|
} |
||||
|
|
||||
|
public IntPtr NSWindow => (Native as IAvnWindowBase)?.ObtainNSWindowHandle() ?? IntPtr.Zero; |
||||
|
|
||||
|
public IntPtr GetNSWindowRetained() |
||||
|
{ |
||||
|
return (Native as IAvnWindowBase)?.ObtainNSWindowHandleRetained() ?? IntPtr.Zero; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class TopLevelImpl : ITopLevelImpl, IFramebufferPlatformSurface |
||||
|
{ |
||||
|
protected IInputRoot? _inputRoot; |
||||
|
private NativeControlHostImpl? _nativeControlHost; |
||||
|
private IStorageProvider? _storageProvider; |
||||
|
private PlatformBehaviorInhibition? _platformBehaviorInhibition; |
||||
|
|
||||
|
private readonly MouseDevice? _mouse; |
||||
|
private readonly IKeyboardDevice? _keyboard; |
||||
|
private readonly ICursorFactory? _cursorFactory; |
||||
|
|
||||
|
protected readonly IAvaloniaNativeFactory Factory; |
||||
|
|
||||
|
private Size _savedLogicalSize; |
||||
|
private double _savedScaling; |
||||
|
private WindowTransparencyLevel _transparencyLevel = WindowTransparencyLevel.None; |
||||
|
|
||||
|
protected MacOSTopLevelHandle? _handle; |
||||
|
|
||||
|
private object _syncRoot = new object(); |
||||
|
private IEnumerable<object>? _surfaces; |
||||
|
|
||||
|
public TopLevelImpl(IAvaloniaNativeFactory factory) |
||||
|
{ |
||||
|
Factory = factory; |
||||
|
|
||||
|
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>(); |
||||
|
_mouse = new MouseDevice(); |
||||
|
_cursorFactory = AvaloniaLocator.Current.GetService<ICursorFactory>(); |
||||
|
} |
||||
|
|
||||
|
internal virtual void Init(MacOSTopLevelHandle handle, IAvnScreens screens) |
||||
|
{ |
||||
|
_handle = handle; |
||||
|
_savedLogicalSize = ClientSize; |
||||
|
_savedScaling = RenderScaling; |
||||
|
_nativeControlHost = new NativeControlHostImpl(Native!.CreateNativeControlHost()); |
||||
|
_storageProvider = new SystemDialogs(this, Factory.CreateSystemDialogs()); |
||||
|
_platformBehaviorInhibition = new PlatformBehaviorInhibition(Factory.CreatePlatformBehaviorInhibition()); |
||||
|
_surfaces = new object[] { new GlPlatformSurface(Native), new MetalPlatformSurface(Native), this }; |
||||
|
|
||||
|
Screen = new ScreenImpl(screens); |
||||
|
InputMethod = new AvaloniaNativeTextInputMethod(Native); |
||||
|
} |
||||
|
|
||||
|
public double DesktopScaling => 1; |
||||
|
|
||||
|
public IAvnTopLevel? Native => _handle?.Native; |
||||
|
public IPlatformHandle? Handle => _handle; |
||||
|
public AvaloniaNativeTextInputMethod? InputMethod { get; private set; } |
||||
|
public Size ClientSize |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (Native == null) |
||||
|
{ |
||||
|
return default; |
||||
|
} |
||||
|
|
||||
|
var s = Native.ClientSize; |
||||
|
return new Size(s.Width, s.Height); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
public double RenderScaling => Native?.Scaling ?? 1; |
||||
|
public IEnumerable<object> Surfaces => _surfaces ?? Array.Empty<object>(); |
||||
|
public Action<RawInputEventArgs>? Input { get; set; } |
||||
|
public Action<Rect>? Paint { get; set; } |
||||
|
public Action<Size, WindowResizeReason>? Resized { get; set; } |
||||
|
public Action<double>? ScalingChanged { get; set; } |
||||
|
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; } |
||||
|
public Compositor Compositor => AvaloniaNativePlatform.Compositor; |
||||
|
public Action? Closed { get; set; } |
||||
|
public Action? LostFocus { get; set; } |
||||
|
|
||||
|
public WindowTransparencyLevel TransparencyLevel |
||||
|
{ |
||||
|
get => _transparencyLevel; |
||||
|
private set |
||||
|
{ |
||||
|
if (_transparencyLevel != value) |
||||
|
{ |
||||
|
_transparencyLevel = value; |
||||
|
TransparencyLevelChanged?.Invoke(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0); |
||||
|
public virtual void SetFrameThemeVariant(PlatformThemeVariant themeVariant) |
||||
|
{ |
||||
|
//noop
|
||||
|
} |
||||
|
|
||||
|
public IMouseDevice? MouseDevice => _mouse; |
||||
|
|
||||
|
public INativeControlHostImpl? NativeControlHost => _nativeControlHost; |
||||
|
|
||||
|
public IScreenImpl? Screen { get; private set; } |
||||
|
|
||||
|
public AutomationPeer? GetAutomationPeer() |
||||
|
{ |
||||
|
return _inputRoot is Control c ? ControlAutomationPeer.CreatePeerForElement(c) : null; |
||||
|
} |
||||
|
|
||||
|
public bool RawTextInputEvent(ulong timeStamp, string text) |
||||
|
{ |
||||
|
if (_inputRoot is null) |
||||
|
return false; |
||||
|
|
||||
|
if (_keyboard is null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); |
||||
|
|
||||
|
var args = new RawTextInputEventArgs(_keyboard, timeStamp, _inputRoot, text); |
||||
|
|
||||
|
Input?.Invoke(args); |
||||
|
|
||||
|
return args.Handled; |
||||
|
} |
||||
|
|
||||
|
public bool RawKeyEvent( |
||||
|
AvnRawKeyEventType type, |
||||
|
ulong timeStamp, |
||||
|
AvnInputModifiers modifiers, |
||||
|
AvnKey key, |
||||
|
AvnPhysicalKey physicalKey, |
||||
|
string keySymbol) |
||||
|
{ |
||||
|
if (_inputRoot is null) |
||||
|
return false; |
||||
|
|
||||
|
if (_keyboard is null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); |
||||
|
|
||||
|
var args = new RawKeyEventArgs( |
||||
|
_keyboard, |
||||
|
timeStamp, |
||||
|
_inputRoot, |
||||
|
(RawKeyEventType)type, |
||||
|
(Key)key, |
||||
|
(RawInputModifiers)modifiers, |
||||
|
(PhysicalKey)physicalKey, |
||||
|
keySymbol); |
||||
|
|
||||
|
Input?.Invoke(args); |
||||
|
|
||||
|
return args.Handled; |
||||
|
} |
||||
|
|
||||
|
public void RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) |
||||
|
{ |
||||
|
if (_inputRoot is null) |
||||
|
return; |
||||
|
|
||||
|
if (_mouse is null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); |
||||
|
|
||||
|
switch (type) |
||||
|
{ |
||||
|
case AvnRawMouseEventType.Wheel: |
||||
|
Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, |
||||
|
point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); |
||||
|
break; |
||||
|
|
||||
|
case AvnRawMouseEventType.Magnify: |
||||
|
Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Magnify, |
||||
|
point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); |
||||
|
break; |
||||
|
|
||||
|
case AvnRawMouseEventType.Rotate: |
||||
|
Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Rotate, |
||||
|
point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); |
||||
|
break; |
||||
|
|
||||
|
case AvnRawMouseEventType.Swipe: |
||||
|
Input?.Invoke(new RawPointerGestureEventArgs(_mouse, timeStamp, _inputRoot, RawPointerEventType.Swipe, |
||||
|
point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (RawInputModifiers)modifiers)); |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
var e = new RawPointerEventArgs(_mouse, timeStamp, _inputRoot, (RawPointerEventType)type, |
||||
|
point.ToAvaloniaPoint(), (RawInputModifiers)modifiers); |
||||
|
|
||||
|
if (!ChromeHitTest(e)) |
||||
|
{ |
||||
|
Input?.Invoke(e); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Invalidate() |
||||
|
{ |
||||
|
Native?.Invalidate(); |
||||
|
} |
||||
|
|
||||
|
public void SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
_inputRoot = inputRoot; |
||||
|
} |
||||
|
|
||||
|
public Point PointToClient(PixelPoint point) |
||||
|
{ |
||||
|
return Native?.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint() ?? default; |
||||
|
} |
||||
|
|
||||
|
public PixelPoint PointToScreen(Point point) |
||||
|
{ |
||||
|
return Native?.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint() ?? default; |
||||
|
} |
||||
|
|
||||
|
public void SetCursor(ICursorImpl? cursor) |
||||
|
{ |
||||
|
if (Native == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var newCursor = cursor as AvaloniaNativeCursor; |
||||
|
newCursor ??= (_cursorFactory?.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor); |
||||
|
Native.SetCursor(newCursor?.Cursor); |
||||
|
} |
||||
|
|
||||
|
public virtual IPopupImpl CreatePopup() |
||||
|
{ |
||||
|
return new PopupImpl(Factory, this); |
||||
|
} |
||||
|
|
||||
|
public void SetTransparencyLevelHint(IReadOnlyList<WindowTransparencyLevel> transparencyLevels) |
||||
|
{ |
||||
|
foreach (var level in transparencyLevels) |
||||
|
{ |
||||
|
AvnWindowTransparencyMode? mode = null; |
||||
|
|
||||
|
if (level == WindowTransparencyLevel.None) |
||||
|
mode = AvnWindowTransparencyMode.Opaque; |
||||
|
if (level == WindowTransparencyLevel.Transparent) |
||||
|
mode = AvnWindowTransparencyMode.Transparent; |
||||
|
else if (level == WindowTransparencyLevel.AcrylicBlur) |
||||
|
mode = AvnWindowTransparencyMode.Blur; |
||||
|
|
||||
|
if (mode.HasValue && level != TransparencyLevel) |
||||
|
{ |
||||
|
Native?.SetTransparencyMode(mode.Value); |
||||
|
TransparencyLevel = level; |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// If we get here, we didn't find a supported level. Use the default of None.
|
||||
|
if (TransparencyLevel != WindowTransparencyLevel.None) |
||||
|
{ |
||||
|
Native?.SetTransparencyMode(AvnWindowTransparencyMode.Opaque); |
||||
|
TransparencyLevel = WindowTransparencyLevel.None; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public virtual object? TryGetFeature(Type featureType) |
||||
|
{ |
||||
|
if (featureType == typeof(ITextInputMethodImpl)) |
||||
|
{ |
||||
|
return InputMethod; |
||||
|
} |
||||
|
|
||||
|
if (featureType == typeof(INativeControlHostImpl)) |
||||
|
{ |
||||
|
return _nativeControlHost; |
||||
|
} |
||||
|
|
||||
|
if (featureType == typeof(IStorageProvider)) |
||||
|
{ |
||||
|
return _storageProvider; |
||||
|
} |
||||
|
|
||||
|
if (featureType == typeof(IPlatformBehaviorInhibition)) |
||||
|
{ |
||||
|
return _platformBehaviorInhibition; |
||||
|
} |
||||
|
|
||||
|
if (featureType == typeof(IClipboard)) |
||||
|
{ |
||||
|
return AvaloniaLocator.Current.GetRequiredService<IClipboard>(); |
||||
|
} |
||||
|
|
||||
|
if (featureType == typeof(ILauncher)) |
||||
|
{ |
||||
|
return new BclLauncher(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public virtual void Dispose() |
||||
|
{ |
||||
|
Native?.Dispose(); |
||||
|
_handle = null; |
||||
|
|
||||
|
_nativeControlHost?.Dispose(); |
||||
|
_nativeControlHost = null; |
||||
|
|
||||
|
(Screen as ScreenImpl)?.Dispose(); |
||||
|
_mouse?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
protected virtual bool ChromeHitTest(RawPointerEventArgs e) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
IFramebufferRenderTarget IFramebufferPlatformSurface.CreateFramebufferRenderTarget() |
||||
|
{ |
||||
|
if (!Dispatcher.UIThread.CheckAccess()) |
||||
|
throw new RenderTargetNotReadyException(); |
||||
|
|
||||
|
var nativeRenderTarget = Native?.CreateSoftwareRenderTarget(); |
||||
|
|
||||
|
if (nativeRenderTarget is null) |
||||
|
{ |
||||
|
throw new RenderTargetNotReadyException(); |
||||
|
} |
||||
|
|
||||
|
return new FramebufferRenderTarget(this, nativeRenderTarget); |
||||
|
} |
||||
|
|
||||
|
protected internal unsafe class TopLevelEvents : NativeCallbackBase, IAvnTopLevelEvents |
||||
|
{ |
||||
|
private readonly TopLevelImpl _parent; |
||||
|
|
||||
|
public TopLevelEvents(TopLevelImpl parent) |
||||
|
{ |
||||
|
_parent = parent; |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.Closed() |
||||
|
{ |
||||
|
var n = _parent.Native; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
_parent?.Closed?.Invoke(); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
|
||||
|
_parent?.Dispose(); |
||||
|
n?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.Paint() |
||||
|
{ |
||||
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); |
||||
|
var s = _parent.ClientSize; |
||||
|
_parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height)); |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.Resized(AvnSize* size, AvnPlatformResizeReason reason) |
||||
|
{ |
||||
|
if (_parent?.Native == null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var s = new Size(size->Width, size->Height); |
||||
|
_parent._savedLogicalSize = s; |
||||
|
_parent.Resized?.Invoke(s, (WindowResizeReason)reason); |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.RawMouseEvent(AvnRawMouseEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) |
||||
|
{ |
||||
|
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); |
||||
|
} |
||||
|
|
||||
|
int IAvnTopLevelEvents.RawKeyEvent(AvnRawKeyEventType type, ulong timeStamp, AvnInputModifiers modifiers, AvnKey key, AvnPhysicalKey physicalKey, string keySymbol) |
||||
|
{ |
||||
|
return _parent.RawKeyEvent(type, timeStamp, modifiers, key, physicalKey, keySymbol).AsComBool(); |
||||
|
} |
||||
|
|
||||
|
int IAvnTopLevelEvents.RawTextInputEvent(ulong timeStamp, string text) |
||||
|
{ |
||||
|
return _parent.RawTextInputEvent(timeStamp, text).AsComBool(); |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.ScalingChanged(double scaling) |
||||
|
{ |
||||
|
_parent._savedScaling = scaling; |
||||
|
_parent.ScalingChanged?.Invoke(scaling); |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.RunRenderPriorityJobs() |
||||
|
{ |
||||
|
Dispatcher.UIThread.RunJobs(DispatcherPriority.UiThreadRender); |
||||
|
} |
||||
|
|
||||
|
void IAvnTopLevelEvents.LostFocus() |
||||
|
{ |
||||
|
_parent.LostFocus?.Invoke(); |
||||
|
} |
||||
|
|
||||
|
AvnDragDropEffects IAvnTopLevelEvents.DragEvent(AvnDragEventType type, AvnPoint position, |
||||
|
AvnInputModifiers modifiers, |
||||
|
AvnDragDropEffects effects, |
||||
|
IAvnClipboard clipboard, IntPtr dataObjectHandle) |
||||
|
{ |
||||
|
var device = AvaloniaLocator.Current.GetService<IDragDropDevice>(); |
||||
|
|
||||
|
if (device is null) |
||||
|
{ |
||||
|
return AvnDragDropEffects.None; |
||||
|
} |
||||
|
|
||||
|
if (_parent._inputRoot is null) |
||||
|
{ |
||||
|
return AvnDragDropEffects.None; |
||||
|
} |
||||
|
|
||||
|
IDataObject? dataObject = null; |
||||
|
if (dataObjectHandle != IntPtr.Zero) |
||||
|
dataObject = GCHandle.FromIntPtr(dataObjectHandle).Target as IDataObject; |
||||
|
|
||||
|
using (var clipboardDataObject = new ClipboardDataObject(clipboard)) |
||||
|
{ |
||||
|
if (dataObject == null) |
||||
|
dataObject = clipboardDataObject; |
||||
|
|
||||
|
var args = new RawDragEvent(device, (RawDragEventType)type, |
||||
|
_parent._inputRoot, position.ToAvaloniaPoint(), dataObject, (DragDropEffects)effects, |
||||
|
(RawInputModifiers)modifiers); |
||||
|
_parent.Input(args); |
||||
|
return (AvnDragDropEffects)args.Effects; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
IAvnAutomationPeer? IAvnTopLevelEvents.AutomationPeer |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
var native = _parent.GetAutomationPeer(); |
||||
|
|
||||
|
return native is null ? null : AvnAutomationPeer.Wrap(native); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class FramebufferRenderTarget : IFramebufferRenderTarget |
||||
|
{ |
||||
|
private readonly TopLevelImpl _parent; |
||||
|
private IAvnSoftwareRenderTarget? _target; |
||||
|
|
||||
|
public FramebufferRenderTarget(TopLevelImpl parent, IAvnSoftwareRenderTarget target) |
||||
|
{ |
||||
|
_parent = parent; |
||||
|
_target = target; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
lock (_parent._syncRoot) |
||||
|
{ |
||||
|
_target?.Dispose(); |
||||
|
_target = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ILockedFramebuffer Lock() |
||||
|
{ |
||||
|
var w = _parent._savedLogicalSize.Width * _parent._savedScaling; |
||||
|
var h = _parent._savedLogicalSize.Height * _parent._savedScaling; |
||||
|
var dpi = _parent._savedScaling * 96; |
||||
|
return new DeferredFramebuffer(_target, cb => |
||||
|
{ |
||||
|
lock (_parent._syncRoot) |
||||
|
{ |
||||
|
if (_parent.Native != null && _target != null) |
||||
|
{ |
||||
|
cb(_parent.Native); |
||||
|
} |
||||
|
} |
||||
|
}, (int)w, (int)h, new Vector(dpi, dpi)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue