Browse Source

Initial implementation of OSX automation.

ui-automation-test
Steven Kirk 5 years ago
parent
commit
d6d583a16e
  1. 8
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  2. 1
      native/Avalonia.Native/src/OSX/AvnString.h
  3. 17
      native/Avalonia.Native/src/OSX/AvnString.mm
  4. 16
      native/Avalonia.Native/src/OSX/automation.h
  5. 220
      native/Avalonia.Native/src/OSX/automation.mm
  6. 4
      native/Avalonia.Native/src/OSX/common.h
  7. 16
      native/Avalonia.Native/src/OSX/main.mm
  8. 1
      native/Avalonia.Native/src/OSX/window.h
  9. 83
      native/Avalonia.Native/src/OSX/window.mm
  10. 36
      src/Avalonia.Native/AutomationNode.cs
  11. 24
      src/Avalonia.Native/AutomationNodeFactory.cs
  12. 97
      src/Avalonia.Native/AvnAutomationPeer.cs
  13. 48
      src/Avalonia.Native/AvnString.cs
  14. 11
      src/Avalonia.Native/Helpers.cs
  15. 4
      src/Avalonia.Native/PopupImpl.cs
  16. 4
      src/Avalonia.Native/WindowImpl.cs
  17. 15
      src/Avalonia.Native/WindowImplBase.cs
  18. 88
      src/Avalonia.Native/avn.idl

8
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -29,6 +29,8 @@
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; };
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -61,6 +63,8 @@
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
BC11A5BC2608D58F0017BAD0 /* automation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = automation.h; sourceTree = "<group>"; };
BC11A5BD2608D58F0017BAD0 /* automation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = automation.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -94,6 +98,8 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
BC11A5BC2608D58F0017BAD0 /* automation.h */,
BC11A5BD2608D58F0017BAD0 /* automation.mm */,
1A1852DB23E05814008F0DED /* deadlock.mm */,
1A002B9D232135EE00021753 /* app.mm */,
37DDA9B121933371002E132B /* AvnString.h */,
@ -138,6 +144,7 @@
buildActionMask = 2147483647;
files = (
37155CE4233C00EB0034DCE9 /* menu.h in Headers */,
BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -207,6 +214,7 @@
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
520624B322973F4100C4DCEF /* menu.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,

1
native/Avalonia.Native/src/OSX/AvnString.h

@ -14,4 +14,5 @@ extern IAvnStringArray* CreateAvnStringArray(NSArray<NSString*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSArray<NSURL*>* array);
extern IAvnStringArray* CreateAvnStringArray(NSString* string);
extern IAvnString* CreateByteArray(void* data, int len);
extern NSString* GetNSStringAndRelease(IAvnString* s);
#endif /* AvnString_h */

17
native/Avalonia.Native/src/OSX/AvnString.mm

@ -141,3 +141,20 @@ IAvnString* CreateByteArray(void* data, int len)
{
return new AvnStringImpl(data, len);
}
NSString* GetNSStringAndRelease(IAvnString* s)
{
if (s != nullptr)
{
char* p;
if (s->Pointer((void**)&p) == S_OK)
{
return [NSString stringWithUTF8String:p];
}
s->Release();
}
return nullptr;
}

16
native/Avalonia.Native/src/OSX/automation.h

@ -0,0 +1,16 @@
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
class IAvnAutomationPeer;
@interface AvnAutomationNode : NSAccessibilityElement
- (AvnAutomationNode *)initWithPeer:(IAvnAutomationPeer *)peer;
@end
struct INSAccessibilityHolder
{
virtual NSObject* _Nonnull GetNSAccessibility () = 0;
};
NS_ASSUME_NONNULL_END

220
native/Avalonia.Native/src/OSX/automation.mm

@ -0,0 +1,220 @@
#import "automation.h"
#include "common.h"
#include "AvnString.h"
#include "window.h"
class AutomationNode : public ComSingleObject<IAvnAutomationNode, &IID_IAvnAutomationNode>,
public INSAccessibilityHolder
{
private:
NSAccessibilityElement* _node;
public:
FORWARD_IUNKNOWN()
AutomationNode(NSAccessibilityElement* node)
{
_node = node;
}
AutomationNode(IAvnAutomationPeer* peer)
{
_node = [[AvnAutomationNode alloc] initWithPeer: peer];
}
virtual NSObject* GetNSAccessibility() override
{
return _node;
}
};
@implementation AvnAutomationNode
{
IAvnAutomationPeer* _peer;
NSMutableArray* _children;
}
- (AvnAutomationNode *)initWithPeer:(IAvnAutomationPeer *)peer
{
self = [super init];
_peer = peer;
return self;
}
- (BOOL)isAccessibilityElement
{
return _peer->IsControlElement();
}
- (NSAccessibilityRole)accessibilityRole
{
auto controlType = _peer->GetAutomationControlType();
switch (controlType) {
case AutomationButton:
return NSAccessibilityButtonRole;
case AutomationCheckBox:
return NSAccessibilityCheckBoxRole;
case AutomationComboBox:
return NSAccessibilityPopUpButtonRole;
case AutomationGroup:
case AutomationPane:
return NSAccessibilityGroupRole;
case AutomationSlider:
return NSAccessibilitySliderRole;
case AutomationTab:
return NSAccessibilityTabGroupRole;
case AutomationTabItem:
return NSAccessibilityRadioButtonRole;
case AutomationWindow:
return NSAccessibilityWindowRole;
default:
return NSAccessibilityUnknownRole;
}
}
- (NSString *)accessibilityIdentifier
{
return GetNSStringAndRelease(_peer->GetAutomationId());
}
- (NSString *)accessibilityTitle
{
return GetNSStringAndRelease(_peer->GetName());
}
- (NSArray *)accessibilityChildren
{
if (_children == nullptr && _peer != nullptr)
{
auto childPeers = _peer->GetChildren();
auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;
if (childCount > 0)
{
_children = [[NSMutableArray alloc] initWithCapacity:childCount];
for (int i = 0; i < childCount; ++i)
{
IAvnAutomationPeer* child;
if (childPeers->Get(i, &child) == S_OK)
{
NSObject* element = ::GetAccessibilityElement(child->GetNode());
[_children addObject:element];
}
}
}
}
return _children;
}
- (NSRect)accessibilityFrame
{
auto view = [self getAvnView];
auto window = [self getAvnWindow];
if (view != nullptr)
{
auto bounds = ToNSRect(_peer->GetBoundingRectangle());
auto windowBounds = [view convertRect:bounds toView:nil];
auto screenBounds = [window convertRectToScreen:windowBounds];
return screenBounds;
}
return NSRect();
}
- (id)accessibilityParent
{
auto parentPeer = _peer->GetParent();
if (parentPeer != nullptr)
{
return GetAccessibilityElement(parentPeer);
}
return [NSApplication sharedApplication];
}
- (id)accessibilityTopLevelUIElement
{
return GetAccessibilityElement([self getRootNode]);
}
- (id)accessibilityWindow
{
return [self accessibilityTopLevelUIElement];
}
- (BOOL)accessibilityPerformPress
{
_peer->InvokeProvider_Invoke();
return YES;
}
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
{
if (selector == @selector(accessibilityPerformPress))
{
return _peer->IsInvokeProvider();
}
return [super isAccessibilitySelectorAllowed:selector];
}
- (IAvnAutomationNode*)getRootNode
{
auto rootPeer = _peer->GetRootPeer();
return rootPeer != nullptr ? rootPeer->GetNode() : nullptr;
}
- (IAvnWindowBase*)getWindow
{
auto rootNode = [self getRootNode];
if (rootNode != nullptr)
{
IAvnWindowBase* window;
if (rootNode->QueryInterface(&IID_IAvnWindow, (void**)&window) == S_OK)
{
return window;
}
}
return nullptr;
}
- (AvnWindow*) getAvnWindow
{
auto window = [self getWindow];
return dynamic_cast<INSWindowHolder*>(window)->GetNSWindow();
}
- (AvnView*) getAvnView
{
auto window = [self getWindow];
return dynamic_cast<INSWindowHolder*>(window)->GetNSView();
}
@end
extern IAvnAutomationNode* CreateAutomationNode(IAvnAutomationPeer* peer)
{
@autoreleasepool
{
return new AutomationNode(peer);
}
}
extern NSObject* GetAccessibilityElement(IAvnAutomationPeer* peer)
{
auto node = peer != nullptr ? peer->GetNode() : nullptr;
return GetAccessibilityElement(node);
}
extern NSObject* GetAccessibilityElement(IAvnAutomationNode* node)
{
auto holder = dynamic_cast<INSAccessibilityHolder*>(node);
return holder != nullptr ? holder->GetNSAccessibility() : nil;
}

4
native/Avalonia.Native/src/OSX/common.h

@ -30,10 +30,14 @@ extern IAvnMenu* GetAppMenu ();
extern NSMenuItem* GetAppMenuItem ();
extern void SetAutoGenerateDefaultAppMenuItems (bool enabled);
extern bool GetAutoGenerateDefaultAppMenuItems ();
extern IAvnAutomationNode* CreateAutomationNode(IAvnAutomationPeer* peer);
extern NSObject* GetAccessibilityElement(IAvnAutomationPeer* peer);
extern NSObject* GetAccessibilityElement(IAvnAutomationNode* node);
extern void InitializeAvnApp(IAvnApplicationEvents* events);
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy;
extern NSPoint ToNSPoint (AvnPoint p);
extern NSRect ToNSRect (AvnRect r);
extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
extern CGFloat PrimaryDisplayHeight();

16
native/Avalonia.Native/src/OSX/main.mm

@ -1,6 +1,7 @@
//This file will contain actual IID structures
#define COM_GUIDS_MATERIALIZE
#include "common.h"
#include "window.h"
static bool s_generateDefaultAppMenuItems = true;
static NSString* s_appTitle = @"Avalonia";
@ -259,6 +260,12 @@ public:
return S_OK;
}
virtual HRESULT CreateAutomationNode (IAvnAutomationPeer* peer, IAvnAutomationNode** ppv) override
{
*ppv = ::CreateAutomationNode(peer);
return S_OK;
}
virtual HRESULT SetAppMenu (IAvnMenu* appMenu) override
{
::SetAppMenu(s_appTitle, appMenu);
@ -295,6 +302,15 @@ NSPoint ToNSPoint (AvnPoint p)
return result;
}
NSRect ToNSRect (AvnRect r)
{
return NSRect
{
NSPoint { r.X, r.Y },
NSSize { r.Width, r.Height }
};
}
AvnPoint ToAvnPoint (NSPoint p)
{
AvnPoint result;

1
native/Avalonia.Native/src/OSX/window.h

@ -39,6 +39,7 @@ class WindowBaseImpl;
struct INSWindowHolder
{
virtual AvnWindow* _Nonnull GetNSWindow () = 0;
virtual AvnView* _Nonnull GetNSView () = 0;
};
struct IWindowStateChanged

83
native/Avalonia.Native/src/OSX/window.mm

@ -5,14 +5,25 @@
#include "menu.h"
#include <OpenGL/gl.h>
#include "rendertarget.h"
#include "AvnString.h"
#include "automation.h"
class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
class WindowBaseImpl : public virtual ComObject,
public virtual IAvnWindowBase,
public virtual IAvnAutomationNode,
public INSWindowHolder,
public INSAccessibilityHolder
{
private:
NSCursor* cursor;
public:
FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP()
INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase)
INTERFACE_MAP_ENTRY(IAvnAutomationNode, IID_IAvnAutomationNode)
END_INTERFACE_MAP()
virtual ~WindowBaseImpl()
{
View = NULL;
@ -104,6 +115,16 @@ public:
{
return Window;
}
virtual AvnView* GetNSView() override
{
return View;
}
virtual NSObject* GetNSAccessibility() override
{
return Window;
}
virtual HRESULT Show(bool activate) override
{
@ -1846,6 +1867,8 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _isExtended;
AvnMenu* _menu;
double _lastScaling;
IAvnAutomationPeer* _automationPeer;
NSMutableArray* _automationChildren;
}
-(void) setIsExtended:(bool)value;
@ -2218,6 +2241,64 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->BaseEvents->PositionChanged(position);
}
}
- (BOOL)isAccessibilityElement
{
[self getAutomationPeer];
return YES;
}
- (NSString *)accessibilityIdentifier
{
auto peer = [self getAutomationPeer];
return GetNSStringAndRelease(peer->GetAutomationId());
}
- (NSArray *)accessibilityChildren
{
auto peer = [self getAutomationPeer];
if (_automationChildren == nullptr)
{
_automationChildren = (NSMutableArray*)[super accessibilityChildren];
auto childPeers = peer->GetChildren();
auto childCount = childPeers != nullptr ? childPeers->GetCount() : 0;
if (childCount > 0)
{
for (int i = 0; i < childCount; ++i)
{
IAvnAutomationPeer* child;
if (childPeers->Get(i, &child) == S_OK)
{
auto element = GetAccessibilityElement(child);
[_automationChildren addObject:element];
}
}
}
}
return _automationChildren;
}
- (id)accessibilityHitTest:(NSPoint)point
{
point = [self convertPointFromScreen:point];
auto p = [_parent->View translateLocalPoint:ToAvnPoint(point)];
auto peer = [self getAutomationPeer];
auto hit = peer->RootProvider_GetPeerFromPoint(p);
return GetAccessibilityElement(hit);
}
- (IAvnAutomationPeer* _Nonnull) getAutomationPeer
{
if (_automationPeer == nullptr)
_automationPeer = _parent->BaseEvents->AutomationStarted(_parent);
return _automationPeer;
}
@end
class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup

36
src/Avalonia.Native/AutomationNode.cs

@ -0,0 +1,36 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Native.Interop;
#nullable enable
namespace Avalonia.Native
{
internal class AutomationNode : IRootAutomationNode
{
public AutomationNode(AutomationNodeFactory factory, IAvnAutomationNode native)
{
Native = native;
Factory = factory;
}
public IAvnAutomationNode Native { get; }
public IAutomationNodeFactory Factory { get; }
public void ChildrenChanged()
{
// TODO
}
public void PropertyChanged(AutomationProperty property, object? oldValue, object? newValue)
{
// TODO
}
public void FocusChanged(AutomationPeer? focus)
{
// TODO
}
}
}

24
src/Avalonia.Native/AutomationNodeFactory.cs

@ -0,0 +1,24 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Native.Interop;
namespace Avalonia.Native
{
internal class AutomationNodeFactory : IAutomationNodeFactory
{
private static AutomationNodeFactory _instance;
private readonly IAvaloniaNativeFactory _native;
public static AutomationNodeFactory GetInstance(IAvaloniaNativeFactory native)
{
return _instance ??= new AutomationNodeFactory(native);
}
private AutomationNodeFactory(IAvaloniaNativeFactory native) => _native = native;
public IAutomationNode CreateNode(AutomationPeer peer)
{
return new AutomationNode(this, _native.CreateAutomationNode(new AvnAutomationPeer(peer)));
}
}
}

97
src/Avalonia.Native/AvnAutomationPeer.cs

@ -0,0 +1,97 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Provider;
using Avalonia.Native.Interop;
#nullable enable
namespace Avalonia.Native
{
internal class AvnAutomationPeer : CallbackBase, IAvnAutomationPeer
{
private readonly AutomationPeer _inner;
public AvnAutomationPeer(AutomationPeer inner) => _inner = inner;
public IAvnAutomationNode Node => ((AutomationNode)_inner.Node).Native;
public IAvnString? AcceleratorKey => _inner.GetAcceleratorKey().ToAvnString();
public IAvnString? AccessKey => _inner.GetAccessKey().ToAvnString();
public AvnAutomationControlType AutomationControlType => (AvnAutomationControlType)_inner.GetAutomationControlType();
public IAvnString? AutomationId => _inner.GetAutomationId().ToAvnString();
public AvnRect BoundingRectangle => _inner.GetBoundingRectangle().ToAvnRect();
public IAvnAutomationPeerArray Children => new AvnAutomationPeerArray(_inner.GetChildren());
public IAvnString ClassName => _inner.GetClassName().ToAvnString();
public IAvnAutomationPeer? LabeledBy => Wrap(_inner.GetLabeledBy());
public IAvnString Name => _inner.GetName().ToAvnString();
public IAvnAutomationPeer? Parent => Wrap(_inner.GetParent());
public int HasKeyboardFocus() => _inner.HasKeyboardFocus().AsComBool();
public int IsContentElement() => _inner.IsContentElement().AsComBool();
public int IsControlElement() => _inner.IsControlElement().AsComBool();
public int IsEnabled() => _inner.IsEnabled().AsComBool();
public int IsKeyboardFocusable() => _inner.IsKeyboardFocusable().AsComBool();
public void SetFocus() => _inner.SetFocus();
public int ShowContextMenu() => _inner.ShowContextMenu().AsComBool();
public IAvnAutomationPeer? RootPeer
{
get
{
var peer = _inner;
var parent = peer.GetParent();
while (!(peer is IRootProvider) && parent is object)
{
peer = parent;
parent = peer.GetParent();
}
return new AvnAutomationPeer(peer);
}
}
public int IsRootProvider() => (_inner is IRootProvider).AsComBool();
public IAvnAutomationPeer? RootProvider_GetPeerFromPoint(AvnPoint point)
{
var result = ((IRootProvider)_inner).GetPeerFromPoint(point.ToAvaloniaPoint());
if (result is null)
return null;
// The OSX accessibility APIs expect non-ignored elements when hit-testing.
while (!result.IsControlElement())
{
var parent = result.GetParent();
if (parent is object)
result = parent;
else
break;
}
return Wrap(result);
}
public int IsInvokeProvider() => (_inner is IInvokeProvider).AsComBool();
public void InvokeProvider_Invoke() => ((IInvokeProvider)_inner).Invoke();
public static AvnAutomationPeer? Wrap(AutomationPeer? peer) =>
peer != null ? new AvnAutomationPeer(peer) : null;
}
internal class AvnAutomationPeerArray : CallbackBase, IAvnAutomationPeerArray
{
private readonly AvnAutomationPeer[] _items;
public AvnAutomationPeerArray(IReadOnlyList<AutomationPeer> items)
{
_items = items.Select(x => new AvnAutomationPeer(x)).ToArray();
}
public uint Count => (uint)_items.Length;
public IAvnAutomationPeer Get(uint index) => _items[index];
}
}

48
src/Avalonia.Native/AvnString.cs

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Avalonia.Native.Interop
{
@ -13,6 +14,53 @@ namespace Avalonia.Native.Interop
{
string[] ToStringArray();
}
internal class AvnString : CallbackBase, IAvnString
{
private IntPtr _native;
private int _nativeLen;
public AvnString(string s) => String = s;
public string String { get; }
public byte[] Bytes => Encoding.UTF8.GetBytes(String);
public unsafe void* Pointer()
{
EnsureNative();
return _native.ToPointer();
}
public int Length()
{
EnsureNative();
return _nativeLen;
}
protected override void Destroyed()
{
if (_native != IntPtr.Zero)
{
Marshal.FreeHGlobal(_native);
_native = IntPtr.Zero;
}
}
private unsafe void EnsureNative()
{
if (string.IsNullOrEmpty(String))
return;
if (_native == IntPtr.Zero)
{
_nativeLen = Encoding.UTF8.GetByteCount(String);
_native = Marshal.AllocHGlobal(_nativeLen + 1);
var ptr = (byte*)_native.ToPointer();
fixed (char* chars = String)
Encoding.UTF8.GetBytes(chars, String.Length, ptr, _nativeLen);
ptr[_nativeLen] = 0;
}
}
}
}
namespace Avalonia.Native.Interop.Impl
{

11
src/Avalonia.Native/Helpers.cs

@ -1,4 +1,5 @@
using Avalonia.Native.Interop;
using JetBrains.Annotations;
namespace Avalonia.Native
{
@ -24,11 +25,21 @@ namespace Avalonia.Native
return new AvnPoint { X = pt.X, Y = pt.Y };
}
public static AvnRect ToAvnRect (this Rect rect)
{
return new AvnRect() { X = rect.X, Y= rect.Y, Height = rect.Height, Width = rect.Width };
}
public static AvnSize ToAvnSize (this Size size)
{
return new AvnSize { Height = size.Height, Width = size.Width };
}
public static IAvnString ToAvnString(this string s)
{
return s != null ? new AvnString(s) : null;
}
public static Size ToAvaloniaSize (this AvnSize size)
{
return new Size(size.Width, size.Height);

4
src/Avalonia.Native/PopupImpl.cs

@ -7,7 +7,6 @@ namespace Avalonia.Native
{
class PopupImpl : WindowBaseImpl, IPopupImpl
{
private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts;
private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
private readonly IWindowBaseImpl _parent;
@ -15,9 +14,8 @@ namespace Avalonia.Native
public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts,
AvaloniaNativePlatformOpenGlInterface glFeature,
IWindowBaseImpl parent) : base(opts, glFeature)
IWindowBaseImpl parent) : base(factory, opts, glFeature)
{
_factory = factory;
_opts = opts;
_glFeature = glFeature;
_parent = parent;

4
src/Avalonia.Native/WindowImpl.cs

@ -13,7 +13,6 @@ namespace Avalonia.Native
{
internal class WindowImpl : WindowBaseImpl, IWindowImpl, ITopLevelImplWithNativeMenuExporter
{
private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts;
private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
IAvnWindow _native;
@ -22,9 +21,8 @@ namespace Avalonia.Native
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
AvaloniaNativePlatformOpenGlInterface glFeature) : base(factory, opts, glFeature)
{
_factory = factory;
_opts = opts;
_glFeature = glFeature;
_doubleClickHelper = new DoubleClickHelper();

15
src/Avalonia.Native/WindowImplBase.cs

@ -48,6 +48,7 @@ namespace Avalonia.Native
internal abstract class WindowBaseImpl : IWindowBaseImpl,
IFramebufferPlatformSurface, ITopLevelImplWithNativeControlHost
{
protected readonly IAvaloniaNativeFactory _factory;
protected IInputRoot _inputRoot;
IAvnWindowBase _native;
private object _syncRoot = new object();
@ -63,8 +64,10 @@ namespace Avalonia.Native
private NativeControlHostImpl _nativeControlHost;
private IGlContext _glContext;
internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature)
internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativePlatformOpenGlInterface glFeature)
{
_factory = factory;
_gpu = opts.UseGpu && glFeature != null;
_deferredRendering = opts.UseDeferredRendering;
@ -92,6 +95,8 @@ namespace Avalonia.Native
Resize(new Size(monitor.WorkingArea.Width * 0.75d, monitor.WorkingArea.Height * 0.7d));
}
public IAvnWindowBase Native => _native;
public Size ClientSize
{
get
@ -244,8 +249,14 @@ namespace Avalonia.Native
return (AvnDragDropEffects)args.Effects;
}
}
}
public IAvnAutomationPeer AutomationStarted(IAvnAutomationNode node)
{
var factory = AutomationNodeFactory.GetInstance(_parent._factory);
return new AvnAutomationPeer(_parent.AutomationStarted(new AutomationNode(factory, node)));
}
}
public void Activate()
{
_native.Activate();

88
src/Avalonia.Native/avn.idl

@ -400,6 +400,49 @@ enum AvnExtendClientAreaChromeHints
AvnDefaultChrome = AvnPreferSystemChrome,
}
enum AvnAutomationControlType
{
AutomationButton,
AutomationCalendar,
AutomationCheckBox,
AutomationComboBox,
AutomationEdit,
AutomationHyperlink,
AutomationImage,
AutomationListItem,
AutomationList,
AutomationMenu,
AutomationMenuBar,
AutomationMenuItem,
AutomationProgressBar,
AutomationRadioButton,
AutomationScrollBar,
AutomationSlider,
AutomationSpinner,
AutomationStatusBar,
AutomationTab,
AutomationTabItem,
AutomationText,
AutomationToolBar,
AutomationToolTip,
AutomationTree,
AutomationTreeItem,
AutomationCustom,
AutomationGroup,
AutomationThumb,
AutomationDataGrid,
AutomationDataItem,
AutomationDocument,
AutomationSplitButton,
AutomationWindow,
AutomationPane,
AutomationHeader,
AutomationHeaderItem,
AutomationTable,
AutomationTitleBar,
AutomationSeparator,
}
[uuid(809c652e-7396-11d2-9771-00a0c9b4d50c)]
interface IAvaloniaNativeFactory : IUnknown
{
@ -418,6 +461,7 @@ interface IAvaloniaNativeFactory : IUnknown
HRESULT CreateMenu(IAvnMenuEvents* cb, IAvnMenu** ppv);
HRESULT CreateMenuItem(IAvnMenuItem** ppv);
HRESULT CreateMenuItemSeparator(IAvnMenuItem** ppv);
HRESULT CreateAutomationNode(IAvnAutomationPeer* peer, IAvnAutomationNode** ppv);
}
[uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)]
@ -506,6 +550,7 @@ interface IAvnWindowBaseEvents : IUnknown
AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers, AvnDragDropEffects effects,
IAvnClipboard* clipboard, [intptr]void* dataObjectHandle);
IAvnAutomationPeer* AutomationStarted(IAvnAutomationNode* node);
}
[uuid(1ae178ee-1fcc-447f-b6dd-b7bb727f934c)]
@ -733,3 +778,46 @@ interface IAvnApplicationEvents : IUnknown
{
void FilesOpened (IAvnStringArray* urls);
}
[uuid(b87016f3-7eec-41de-b385-07844c268dc4)]
interface IAvnAutomationPeer : IUnknown
{
IAvnAutomationNode* GetNode();
IAvnString* GetAcceleratorKey();
IAvnString* GetAccessKey();
AvnAutomationControlType GetAutomationControlType();
IAvnString* GetAutomationId();
AvnRect GetBoundingRectangle();
IAvnAutomationPeerArray* GetChildren();
IAvnString* GetClassName();
IAvnAutomationPeer* GetLabeledBy();
IAvnString* GetName();
IAvnAutomationPeer* GetParent();
bool HasKeyboardFocus();
bool IsContentElement();
bool IsControlElement();
bool IsEnabled();
bool IsKeyboardFocusable();
void SetFocus();
bool ShowContextMenu();
IAvnAutomationPeer* GetRootPeer();
bool IsRootProvider();
IAvnAutomationPeer* RootProvider_GetPeerFromPoint(AvnPoint point);
bool IsInvokeProvider();
void InvokeProvider_Invoke();
}
[uuid(b00af5da-78af-4b33-bfff-4ce13a6239a9)]
interface IAvnAutomationPeerArray : IUnknown
{
uint GetCount();
HRESULT Get(uint index, IAvnAutomationPeer**ppv);
}
[uuid(004dc40b-e435-49dc-bac5-6272ee35382a)]
interface IAvnAutomationNode : IUnknown
{
}

Loading…
Cancel
Save