Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS: Don't include two windows in a11y tree. #15899

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
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 */; };
BC7C33822C066DBF00945A48 /* AvnAutomationNode.h in Headers */ = {isa = PBXBuildFile; fileRef = BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */; };
ED3791C42862E1F40080BD62 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */; };
ED754D262A97306B0078B4DF /* PlatformRenderTimer.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */; };
EDF8CDCD2964CB01001EE34F /* PlatformSettings.mm in Sources */ = {isa = PBXBuildFile; fileRef = EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */; };
Expand Down Expand Up @@ -122,6 +123,8 @@
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>"; };
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnAutomationNode.h; sourceTree = "<group>"; };
BC7C33832C066F1100945A48 /* AvnAccessibility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnAccessibility.h; sourceTree = "<group>"; };
ED3791C32862E1F40080BD62 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformRenderTimer.mm; sourceTree = "<group>"; };
EDF8CDCC2964CB01001EE34F /* PlatformSettings.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformSettings.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -164,6 +167,8 @@
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
BC7C33832C066F1100945A48 /* AvnAccessibility.h */,
BC7C33812C066DBF00945A48 /* AvnAutomationNode.h */,
ED754D252A97306B0078B4DF /* PlatformRenderTimer.mm */,
855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */,
8D2F3511292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h */,
Expand Down Expand Up @@ -241,6 +246,7 @@
183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */,
1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */,
183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */,
BC7C33822C066DBF00945A48 /* AvnAutomationNode.h in Headers */,
18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */,
8D300D65292D0A6800320C49 /* AvnTextInputMethod.h in Headers */,
8D2F3512292F6AAE007FCF54 /* AvnTextInputMethodDelegate.h in Headers */,
Expand Down
12 changes: 12 additions & 0 deletions native/Avalonia.Native/src/OSX/AvnAccessibility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once
#import <Cocoa/Cocoa.h>

// Defines the interface between AvnAutomationNode and objects which implement
// NSAccessibility such as AvnAccessibilityElement or AvnWindow.
@protocol AvnAccessibility <NSAccessibility>
@required
- (void) raiseChildrenChanged;
@optional
- (void) raiseFocusChanged;
- (void) raisePropertyChanged:(AvnAutomationProperty)property;
@end
18 changes: 18 additions & 0 deletions native/Avalonia.Native/src/OSX/AvnAutomationNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include "avalonia-native.h"
#include "AvnAccessibility.h"

// Defines a means for managed code to raise accessibility events.
class AvnAutomationNode : public ComSingleObject<IAvnAutomationNode, &IID_IAvnAutomationNode>
{
public:
FORWARD_IUNKNOWN()
AvnAutomationNode(id <AvnAccessibility> owner) { _owner = owner; }
AvnAccessibilityElement* GetOwner() { return _owner; }
virtual void Dispose() override { _owner = nil; }
virtual void ChildrenChanged () override { [_owner raiseChildrenChanged]; }
virtual void PropertyChanged (AvnAutomationProperty property) override { [_owner raisePropertyChanged:property]; }
virtual void FocusChanged () override { [_owner raiseFocusChanged]; }
private:
__strong id <AvnAccessibility> _owner;
};
1 change: 1 addition & 0 deletions native/Avalonia.Native/src/OSX/AvnView.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
-(AvnPlatformResizeReason) getResizeReason;
-(void) setResizeReason:(AvnPlatformResizeReason)reason;
-(void) setRenderTarget:(NSObject<IRenderTarget>*)target;
-(void) raiseAccessibilityChildrenChanged;
+ (AvnPoint)toAvnPoint:(CGPoint)p;
@end
77 changes: 58 additions & 19 deletions native/Avalonia.Native/src/OSX/AvnView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ @implementation AvnView
AvnPixelSize _lastPixelSize;
NSObject<IRenderTarget>* _currentRenderTarget;
AvnPlatformResizeReason _resizeReason;
AvnAccessibilityElement* _accessibilityChild;
NSRect _cursorRect;
NSMutableAttributedString* _text;
NSRange _selectedRange;
NSRange _markedRange;
NSEvent* _lastKeyDownEvent;
NSMutableArray* _accessibilityChildren;
}

- (void)onClosed
Expand Down Expand Up @@ -795,35 +795,74 @@ - (void)setResizeReason:(AvnPlatformResizeReason)reason
_resizeReason = reason;
}

- (AvnAccessibilityElement *) accessibilityChild
- (NSArray *)accessibilityChildren
{
if (_accessibilityChild == nil)
{
auto peer = _parent->BaseEvents->GetAutomationPeer();
if (_accessibilityChildren == nil)
[self recalculateAccessibiltyChildren];
return _accessibilityChildren;
}

if (peer == nil)
return nil;
- (id _Nullable) accessibilityHitTest:(NSPoint)point
{
if (![[self window] isKindOfClass:[AvnWindow class]])
return self;

_accessibilityChild = [AvnAccessibilityElement acquire:peer];
}
auto window = (AvnWindow*)[self window];
auto peer = [window automationPeer];

return _accessibilityChild;
}
if (!peer->IsRootProvider())
return nil;

- (NSArray *)accessibilityChildren
{
auto child = [self accessibilityChild];
return NSAccessibilityUnignoredChildrenForOnlyChild(child);
auto clientPoint = [window convertPointFromScreen:point];
auto localPoint = [self translateLocalPoint:ToAvnPoint(clientPoint)];
auto hit = peer->RootProvider_GetPeerFromPoint(localPoint);
return [AvnAccessibilityElement acquire:hit];
}

- (id)accessibilityHitTest:(NSPoint)point
- (void)raiseAccessibilityChildrenChanged
{
return [[self accessibilityChild] accessibilityHitTest:point];
auto changed = _accessibilityChildren ? [NSMutableSet setWithArray:_accessibilityChildren] : [NSMutableSet set];

[self recalculateAccessibiltyChildren];

if (_accessibilityChildren)
[changed addObjectsFromArray:_accessibilityChildren];

NSAccessibilityPostNotificationWithUserInfo(
self,
NSAccessibilityLayoutChangedNotification,
@{ NSAccessibilityUIElementsKey: [changed allObjects]});
}

- (id)accessibilityFocusedUIElement
- (void)recalculateAccessibiltyChildren
{
return [[self accessibilityChild] accessibilityFocusedUIElement];
_accessibilityChildren = [[NSMutableArray alloc] init];

if (![[self window] isKindOfClass:[AvnWindow class]])
{
return;
}

// The accessibility children of the Window are exposed as children
// of the AvnView.
auto window = (AvnWindow*)[self window];
auto peer = [window automationPeer];
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)
{
id element = [AvnAccessibilityElement acquire:child];
[_accessibilityChildren addObject:element];
}
}
}
}

- (void) setText:(NSString *)text{
Expand Down
47 changes: 46 additions & 1 deletion native/Avalonia.Native/src/OSX/AvnWindow.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "AvnView.h"
#include "WindowInterfaces.h"
#include "PopupImpl.h"
#include "AvnAutomationNode.h"
#include "AvnString.h"

@implementation CLASS_NAME
{
Expand All @@ -35,6 +37,13 @@ @implementation CLASS_NAME
bool _isExtended;
bool _isTransitioningToFullScreen;
AvnMenu* _menu;
IAvnAutomationPeer* _automationPeer;
AvnAutomationNode* _automationNode;
}

-(AvnView* _Nullable) view
{
return _parent->View;
}

-(void) setIsExtended:(bool)value;
Expand Down Expand Up @@ -211,7 +220,7 @@ - (void)windowWillClose:(NSNotification *_Nonnull)notification
ComPtr<WindowBaseImpl> parent = _parent;
_parent = NULL;

auto window = dynamic_cast<WindowImpl*>(parent.getRaw());
auto window = dynamic_cast<WindowImpl*>(parent.getRaw());

if(window != nullptr)
{
Expand Down Expand Up @@ -492,5 +501,41 @@ - (void)disconnectParent {
_parent = nullptr;
}

- (id _Nullable) accessibilityFocusedUIElement
{
if (![self automationPeer]->IsRootProvider())
return nil;
auto focusedPeer = [self automationPeer]->RootProvider_GetFocus();
return [AvnAccessibilityElement acquire:focusedPeer];
}

- (NSString * _Nullable) accessibilityIdentifier
{
return GetNSStringAndRelease([self automationPeer]->GetAutomationId());
}

- (IAvnAutomationPeer* _Nonnull) automationPeer
{
if (_automationPeer == nullptr)
{
_automationPeer = _parent->BaseEvents->GetAutomationPeer();
_automationNode = new AvnAutomationNode(self);
_automationPeer->SetNode(_automationNode);
}

return _automationPeer;
}

- (void)raiseChildrenChanged
{
[_parent->View raiseAccessibilityChildrenChanged];
}

- (void)raiseFocusChanged
{
id focused = [self accessibilityFocusedUIElement];
NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification);
}

@end

8 changes: 5 additions & 3 deletions native/Avalonia.Native/src/OSX/WindowInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
#import <AppKit/AppKit.h>
#include "WindowProtocol.h"
#include "WindowBaseImpl.h"
#include "AvnAccessibility.h"

@interface AvnWindow : NSWindow <AvnWindowProtocol, NSWindowDelegate>
@interface AvnWindow : NSWindow <AvnWindowProtocol, NSWindowDelegate, AvnAccessibility>
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
-(AvnView* _Nullable) view;
@end

@interface AvnPanel : NSPanel <AvnWindowProtocol, NSWindowDelegate>
@interface AvnPanel : NSPanel <AvnWindowProtocol, NSWindowDelegate, AvnAccessibility>
-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask;
@end
@end
2 changes: 2 additions & 0 deletions native/Avalonia.Native/src/OSX/WindowProtocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import <AppKit/AppKit.h>

@class AvnMenu;
struct IAvnAutomationPeer;

@protocol AvnWindowProtocol
-(void) pollModalSession: (NSModalSession _Nonnull) session;
Expand All @@ -16,6 +17,7 @@
-(void) showAppMenuOnly;
-(void) showWindowMenuWithAppMenu;
-(void) applyMenu:(AvnMenu* _Nullable)menu;
-(IAvnAutomationPeer* _Nonnull) automationPeer;

-(double) getExtendedTitleBarHeight;
-(void) setIsExtended:(bool)value;
Expand Down
5 changes: 3 additions & 2 deletions native/Avalonia.Native/src/OSX/automation.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#pragma once

#import <Cocoa/Cocoa.h>
#include "AvnAccessibility.h"
NS_ASSUME_NONNULL_BEGIN

class IAvnAutomationPeer;

@interface AvnAccessibilityElement : NSAccessibilityElement
+ (AvnAccessibilityElement *) acquire:(IAvnAutomationPeer *) peer;
@interface AvnAccessibilityElement : NSAccessibilityElement <AvnAccessibility>
+ (id _Nullable) acquire:(IAvnAutomationPeer *) peer;
@end

NS_ASSUME_NONNULL_END
Loading
Loading