committed by
GitHub
414 changed files with 12341 additions and 2348 deletions
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -0,0 +1,5 @@ |
|||
<ProjectConfiguration> |
|||
<Settings> |
|||
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely> |
|||
</Settings> |
|||
</ProjectConfiguration> |
|||
@ -1,7 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.178" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.178" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.178"/> |
|||
<PackageReference Include="HarfBuzzSharp" Version="2.8.2-preview.209" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.Linux" Version="2.8.2-preview.209" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="HarfBuzzSharp.NativeAssets.WebAssembly" Version="2.8.2-preview.209"/> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,7 +1,7 @@ |
|||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<ItemGroup> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.178" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.178" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.178"/> |
|||
<PackageReference Include="SkiaSharp" Version="2.88.0-preview.209" /> |
|||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.88.0-preview.209" /> |
|||
<PackageReference Condition="'$(IncludeWasmSkia)' == 'true'" Include="SkiaSharp.NativeAssets.WebAssembly" Version="2.88.0-preview.209"/> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -0,0 +1,12 @@ |
|||
#import <Cocoa/Cocoa.h> |
|||
#include "window.h" |
|||
|
|||
NS_ASSUME_NONNULL_BEGIN |
|||
|
|||
class IAvnAutomationPeer; |
|||
|
|||
@interface AvnAccessibilityElement : NSAccessibilityElement |
|||
+ (AvnAccessibilityElement *) acquire:(IAvnAutomationPeer *) peer; |
|||
@end |
|||
|
|||
NS_ASSUME_NONNULL_END |
|||
@ -0,0 +1,496 @@ |
|||
#include "common.h" |
|||
#include "automation.h" |
|||
#include "AvnString.h" |
|||
#include "window.h" |
|||
|
|||
@interface AvnAccessibilityElement (Events) |
|||
- (void) raiseChildrenChanged; |
|||
@end |
|||
|
|||
@interface AvnRootAccessibilityElement : AvnAccessibilityElement |
|||
- (AvnView *) ownerView; |
|||
- (AvnRootAccessibilityElement *) initWithPeer:(IAvnAutomationPeer *) peer owner:(AvnView*) owner; |
|||
- (void) raiseFocusChanged; |
|||
@end |
|||
|
|||
class AutomationNode : public ComSingleObject<IAvnAutomationNode, &IID_IAvnAutomationNode> |
|||
{ |
|||
public: |
|||
FORWARD_IUNKNOWN() |
|||
|
|||
AutomationNode(AvnAccessibilityElement* 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 |
|||
{ |
|||
|
|||
} |
|||
|
|||
virtual void FocusChanged () override |
|||
{ |
|||
[(AvnRootAccessibilityElement*)_owner raiseFocusChanged]; |
|||
} |
|||
|
|||
private: |
|||
__strong AvnAccessibilityElement* _owner; |
|||
}; |
|||
|
|||
@implementation AvnAccessibilityElement |
|||
{ |
|||
IAvnAutomationPeer* _peer; |
|||
AutomationNode* _node; |
|||
NSMutableArray* _children; |
|||
} |
|||
|
|||
+ (AvnAccessibilityElement *)acquire:(IAvnAutomationPeer *)peer |
|||
{ |
|||
if (peer == nullptr) |
|||
return nil; |
|||
|
|||
auto instance = peer->GetNode(); |
|||
|
|||
if (instance != nullptr) |
|||
return dynamic_cast<AutomationNode*>(instance)->GetOwner(); |
|||
|
|||
if (peer->IsRootProvider()) |
|||
{ |
|||
auto window = peer->RootProvider_GetWindow(); |
|||
auto holder = dynamic_cast<INSWindowHolder*>(window); |
|||
auto view = holder->GetNSView(); |
|||
return [[AvnRootAccessibilityElement alloc] initWithPeer:peer owner:view]; |
|||
} |
|||
else |
|||
{ |
|||
return [[AvnAccessibilityElement alloc] initWithPeer:peer]; |
|||
} |
|||
} |
|||
|
|||
- (AvnAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer |
|||
{ |
|||
self = [super init]; |
|||
_peer = peer; |
|||
_node = new AutomationNode(self); |
|||
_peer->SetNode(_node); |
|||
return self; |
|||
} |
|||
|
|||
- (void)dealloc |
|||
{ |
|||
if (_node) |
|||
delete _node; |
|||
_node = nullptr; |
|||
} |
|||
|
|||
- (NSString *)description |
|||
{ |
|||
return [NSString stringWithFormat:@"%@ '%@' (%p)", |
|||
GetNSStringAndRelease(_peer->GetClassName()), |
|||
GetNSStringAndRelease(_peer->GetName()), |
|||
_peer]; |
|||
} |
|||
|
|||
- (IAvnAutomationPeer *)peer |
|||
{ |
|||
return _peer; |
|||
} |
|||
|
|||
- (BOOL)isAccessibilityElement |
|||
{ |
|||
return _peer->IsControlElement(); |
|||
} |
|||
|
|||
- (NSAccessibilityRole)accessibilityRole |
|||
{ |
|||
auto controlType = _peer->GetAutomationControlType(); |
|||
|
|||
switch (controlType) { |
|||
case AutomationButton: return NSAccessibilityButtonRole; |
|||
case AutomationCalendar: return NSAccessibilityGridRole; |
|||
case AutomationCheckBox: return NSAccessibilityCheckBoxRole; |
|||
case AutomationComboBox: return NSAccessibilityPopUpButtonRole; |
|||
case AutomationComboBoxItem: return NSAccessibilityMenuItemRole; |
|||
case AutomationEdit: return NSAccessibilityTextFieldRole; |
|||
case AutomationHyperlink: return NSAccessibilityLinkRole; |
|||
case AutomationImage: return NSAccessibilityImageRole; |
|||
case AutomationListItem: return NSAccessibilityRowRole; |
|||
case AutomationList: return NSAccessibilityTableRole; |
|||
case AutomationMenu: return NSAccessibilityMenuBarRole; |
|||
case AutomationMenuBar: return NSAccessibilityMenuBarRole; |
|||
case AutomationMenuItem: return NSAccessibilityMenuItemRole; |
|||
case AutomationProgressBar: return NSAccessibilityProgressIndicatorRole; |
|||
case AutomationRadioButton: return NSAccessibilityRadioButtonRole; |
|||
case AutomationScrollBar: return NSAccessibilityScrollBarRole; |
|||
case AutomationSlider: return NSAccessibilitySliderRole; |
|||
case AutomationSpinner: return NSAccessibilityIncrementorRole; |
|||
case AutomationStatusBar: return NSAccessibilityTableRole; |
|||
case AutomationTab: return NSAccessibilityTabGroupRole; |
|||
case AutomationTabItem: return NSAccessibilityRadioButtonRole; |
|||
case AutomationText: return NSAccessibilityStaticTextRole; |
|||
case AutomationToolBar: return NSAccessibilityToolbarRole; |
|||
case AutomationToolTip: return NSAccessibilityPopoverRole; |
|||
case AutomationTree: return NSAccessibilityOutlineRole; |
|||
case AutomationTreeItem: return NSAccessibilityCellRole; |
|||
case AutomationCustom: return NSAccessibilityUnknownRole; |
|||
case AutomationGroup: return NSAccessibilityGroupRole; |
|||
case AutomationThumb: return NSAccessibilityHandleRole; |
|||
case AutomationDataGrid: return NSAccessibilityGridRole; |
|||
case AutomationDataItem: return NSAccessibilityCellRole; |
|||
case AutomationDocument: return NSAccessibilityStaticTextRole; |
|||
case AutomationSplitButton: return NSAccessibilityPopUpButtonRole; |
|||
case AutomationWindow: return NSAccessibilityWindowRole; |
|||
case AutomationPane: return NSAccessibilityGroupRole; |
|||
case AutomationHeader: return NSAccessibilityGroupRole; |
|||
case AutomationHeaderItem: return NSAccessibilityButtonRole; |
|||
case AutomationTable: return NSAccessibilityTableRole; |
|||
case AutomationTitleBar: return NSAccessibilityGroupRole; |
|||
// Treat unknown roles as generic group container items. Returning |
|||
// NSAccessibilityUnknownRole is also possible but makes the screen |
|||
// reader focus on the item instead of passing focus to child items. |
|||
default: return NSAccessibilityGroupRole; |
|||
} |
|||
} |
|||
|
|||
- (NSString *)accessibilityIdentifier |
|||
{ |
|||
return GetNSStringAndRelease(_peer->GetAutomationId()); |
|||
} |
|||
|
|||
- (NSString *)accessibilityTitle |
|||
{ |
|||
// StaticText exposes its text via the value property. |
|||
if (_peer->GetAutomationControlType() != AutomationText) |
|||
{ |
|||
return GetNSStringAndRelease(_peer->GetName()); |
|||
} |
|||
|
|||
return [super accessibilityTitle]; |
|||
} |
|||
|
|||
- (id)accessibilityValue |
|||
{ |
|||
if (_peer->IsRangeValueProvider()) |
|||
{ |
|||
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetValue()]; |
|||
} |
|||
else if (_peer->IsToggleProvider()) |
|||
{ |
|||
switch (_peer->ToggleProvider_GetToggleState()) { |
|||
case 0: return [NSNumber numberWithBool:NO]; |
|||
case 1: return [NSNumber numberWithBool:YES]; |
|||
default: return [NSNumber numberWithInt:2]; |
|||
} |
|||
} |
|||
else if (_peer->IsValueProvider()) |
|||
{ |
|||
return GetNSStringAndRelease(_peer->ValueProvider_GetValue()); |
|||
} |
|||
else if (_peer->GetAutomationControlType() == AutomationText) |
|||
{ |
|||
return GetNSStringAndRelease(_peer->GetName()); |
|||
} |
|||
|
|||
return [super accessibilityValue]; |
|||
} |
|||
|
|||
- (id)accessibilityMinValue |
|||
{ |
|||
if (_peer->IsRangeValueProvider()) |
|||
{ |
|||
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMinimum()]; |
|||
} |
|||
|
|||
return [super accessibilityMinValue]; |
|||
} |
|||
|
|||
- (id)accessibilityMaxValue |
|||
{ |
|||
if (_peer->IsRangeValueProvider()) |
|||
{ |
|||
return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetMaximum()]; |
|||
} |
|||
|
|||
return [super accessibilityMaxValue]; |
|||
} |
|||
|
|||
- (BOOL)isAccessibilityEnabled |
|||
{ |
|||
return _peer->IsEnabled(); |
|||
} |
|||
|
|||
- (BOOL)isAccessibilityFocused |
|||
{ |
|||
return _peer->HasKeyboardFocus(); |
|||
} |
|||
|
|||
- (NSArray *)accessibilityChildren |
|||
{ |
|||
if (_children == nullptr && _peer != nullptr) |
|||
[self recalculateChildren]; |
|||
return _children; |
|||
} |
|||
|
|||
- (NSRect)accessibilityFrame |
|||
{ |
|||
id topLevel = [self accessibilityTopLevelUIElement]; |
|||
auto result = NSZeroRect; |
|||
|
|||
if ([topLevel isKindOfClass:[AvnRootAccessibilityElement class]]) |
|||
{ |
|||
auto root = (AvnRootAccessibilityElement*)topLevel; |
|||
auto view = [root ownerView]; |
|||
|
|||
if (view) |
|||
{ |
|||
auto window = [view window]; |
|||
auto bounds = ToNSRect(_peer->GetBoundingRectangle()); |
|||
auto windowBounds = [view convertRect:bounds toView:nil]; |
|||
auto screenBounds = [window convertRectToScreen:windowBounds]; |
|||
result = screenBounds; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
- (id)accessibilityParent |
|||
{ |
|||
auto parentPeer = _peer->GetParent(); |
|||
return parentPeer ? [AvnAccessibilityElement acquire:parentPeer] : [NSApplication sharedApplication]; |
|||
} |
|||
|
|||
- (id)accessibilityTopLevelUIElement |
|||
{ |
|||
auto rootPeer = _peer->GetRootPeer(); |
|||
return [AvnAccessibilityElement acquire:rootPeer]; |
|||
} |
|||
|
|||
- (id)accessibilityWindow |
|||
{ |
|||
id topLevel = [self accessibilityTopLevelUIElement]; |
|||
return [topLevel isKindOfClass:[NSWindow class]] ? topLevel : nil; |
|||
} |
|||
|
|||
- (BOOL)isAccessibilityExpanded |
|||
{ |
|||
if (!_peer->IsExpandCollapseProvider()) |
|||
return NO; |
|||
return _peer->ExpandCollapseProvider_GetIsExpanded(); |
|||
} |
|||
|
|||
- (void)setAccessibilityExpanded:(BOOL)accessibilityExpanded |
|||
{ |
|||
if (!_peer->IsExpandCollapseProvider()) |
|||
return; |
|||
if (accessibilityExpanded) |
|||
_peer->ExpandCollapseProvider_Expand(); |
|||
else |
|||
_peer->ExpandCollapseProvider_Collapse(); |
|||
} |
|||
|
|||
- (BOOL)accessibilityPerformPress |
|||
{ |
|||
if (_peer->IsInvokeProvider()) |
|||
{ |
|||
_peer->InvokeProvider_Invoke(); |
|||
} |
|||
else if (_peer->IsExpandCollapseProvider()) |
|||
{ |
|||
_peer->ExpandCollapseProvider_Expand(); |
|||
} |
|||
else if (_peer->IsToggleProvider()) |
|||
{ |
|||
_peer->ToggleProvider_Toggle(); |
|||
} |
|||
return YES; |
|||
} |
|||
|
|||
- (BOOL)accessibilityPerformIncrement |
|||
{ |
|||
if (!_peer->IsRangeValueProvider()) |
|||
return NO; |
|||
auto value = _peer->RangeValueProvider_GetValue(); |
|||
value += _peer->RangeValueProvider_GetSmallChange(); |
|||
_peer->RangeValueProvider_SetValue(value); |
|||
return YES; |
|||
} |
|||
|
|||
- (BOOL)accessibilityPerformDecrement |
|||
{ |
|||
if (!_peer->IsRangeValueProvider()) |
|||
return NO; |
|||
auto value = _peer->RangeValueProvider_GetValue(); |
|||
value -= _peer->RangeValueProvider_GetSmallChange(); |
|||
_peer->RangeValueProvider_SetValue(value); |
|||
return YES; |
|||
} |
|||
|
|||
- (BOOL)accessibilityPerformShowMenu |
|||
{ |
|||
if (!_peer->IsExpandCollapseProvider()) |
|||
return NO; |
|||
_peer->ExpandCollapseProvider_Expand(); |
|||
return YES; |
|||
} |
|||
|
|||
- (BOOL)isAccessibilitySelected |
|||
{ |
|||
if (_peer->IsSelectionItemProvider()) |
|||
return _peer->SelectionItemProvider_IsSelected(); |
|||
return NO; |
|||
} |
|||
|
|||
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector |
|||
{ |
|||
if (selector == @selector(accessibilityPerformShowMenu)) |
|||
{ |
|||
return _peer->IsExpandCollapseProvider() && _peer->ExpandCollapseProvider_GetShowsMenu(); |
|||
} |
|||
else if (selector == @selector(isAccessibilityExpanded)) |
|||
{ |
|||
return _peer->IsExpandCollapseProvider(); |
|||
} |
|||
else if (selector == @selector(accessibilityPerformPress)) |
|||
{ |
|||
return _peer->IsInvokeProvider() || _peer->IsExpandCollapseProvider() || _peer->IsToggleProvider(); |
|||
} |
|||
else if (selector == @selector(accessibilityPerformIncrement) || |
|||
selector == @selector(accessibilityPerformDecrement) || |
|||
selector == @selector(accessibilityMinValue) || |
|||
selector == @selector(accessibilityMaxValue)) |
|||
{ |
|||
return _peer->IsRangeValueProvider(); |
|||
} |
|||
|
|||
return [super isAccessibilitySelectorAllowed:selector]; |
|||
} |
|||
|
|||
- (void)raiseChildrenChanged |
|||
{ |
|||
auto changed = _children ? [NSMutableSet setWithArray:_children] : [NSMutableSet set]; |
|||
|
|||
[self recalculateChildren]; |
|||
|
|||
if (_children) |
|||
[changed addObjectsFromArray:_children]; |
|||
|
|||
NSAccessibilityPostNotificationWithUserInfo( |
|||
self, |
|||
NSAccessibilityLayoutChangedNotification, |
|||
@{ NSAccessibilityUIElementsKey: [changed allObjects]}); |
|||
} |
|||
|
|||
- (void)raisePropertyChanged |
|||
{ |
|||
} |
|||
|
|||
- (void)setAccessibilityFocused:(BOOL)accessibilityFocused |
|||
{ |
|||
if (accessibilityFocused) |
|||
_peer->SetFocus(); |
|||
} |
|||
|
|||
- (void)recalculateChildren |
|||
{ |
|||
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) |
|||
{ |
|||
auto element = [AvnAccessibilityElement acquire:child]; |
|||
[_children addObject:element]; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
_children = nil; |
|||
} |
|||
} |
|||
|
|||
@end |
|||
|
|||
@implementation AvnRootAccessibilityElement |
|||
{ |
|||
AvnView* _owner; |
|||
} |
|||
|
|||
- (AvnRootAccessibilityElement *)initWithPeer:(IAvnAutomationPeer *)peer owner:(AvnView *)owner |
|||
{ |
|||
self = [super initWithPeer:peer]; |
|||
_owner = owner; |
|||
|
|||
// Seems we need to raise a focus changed notification here if we have focus |
|||
auto focusedPeer = [self peer]->RootProvider_GetFocus(); |
|||
id focused = [AvnAccessibilityElement acquire:focusedPeer]; |
|||
|
|||
if (focused) |
|||
NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification); |
|||
|
|||
return self; |
|||
} |
|||
|
|||
- (AvnView *)ownerView |
|||
{ |
|||
return _owner; |
|||
} |
|||
|
|||
- (id)accessibilityFocusedUIElement |
|||
{ |
|||
auto focusedPeer = [self peer]->RootProvider_GetFocus(); |
|||
return [AvnAccessibilityElement acquire:focusedPeer]; |
|||
} |
|||
|
|||
- (id)accessibilityHitTest:(NSPoint)point |
|||
{ |
|||
auto clientPoint = [[_owner window] convertPointFromScreen:point]; |
|||
auto localPoint = [_owner translateLocalPoint:ToAvnPoint(clientPoint)]; |
|||
auto hit = [self peer]->RootProvider_GetPeerFromPoint(localPoint); |
|||
return [AvnAccessibilityElement acquire:hit]; |
|||
} |
|||
|
|||
- (id)accessibilityParent |
|||
{ |
|||
return _owner; |
|||
} |
|||
|
|||
- (void)raiseFocusChanged |
|||
{ |
|||
id focused = [self accessibilityFocusedUIElement]; |
|||
NSAccessibilityPostNotification(focused, NSAccessibilityFocusedUIElementChangedNotification); |
|||
} |
|||
|
|||
// Although this method is marked as deprecated we get runtime warnings if we don't handle it. |
|||
#pragma clang diagnostic push |
|||
#pragma clang diagnostic ignored "-Wdeprecated-implementations" |
|||
- (void)accessibilityPerformAction:(NSAccessibilityActionName)action |
|||
{ |
|||
[_owner accessibilityPerformAction:action]; |
|||
} |
|||
#pragma clang diagnostic pop |
|||
|
|||
@end |
|||
@ -0,0 +1,7 @@ |
|||
<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> |
|||
@ -1,186 +1,16 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform> |
|||
<ProjectGuid>{57E0455D-D565-44BB-B069-EE1AA20F8337}</ProjectGuid> |
|||
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
|||
<OutputType>Exe</OutputType> |
|||
<RootNamespace>ControlCatalog.iOS</RootNamespace> |
|||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix> |
|||
<AssemblyName>ControlCatalogiOS</AssemblyName> |
|||
<MtouchEnableSGenConc>true</MtouchEnableSGenConc> |
|||
<MtouchHttpClientHandler>NSUrlSessionHandler</MtouchHttpClientHandler> |
|||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle> |
|||
<ProvisioningType>automatic</ProvisioningType> |
|||
<ProvisioningType>manual</ProvisioningType> |
|||
<TargetFramework>net6.0-ios</TargetFramework> |
|||
<SupportedOSPlatformVersion>10.0</SupportedOSPlatformVersion> |
|||
<!-- temporal workaround for our GL interface backend --> |
|||
<UseInterpreter>True</UseInterpreter> |
|||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier> |
|||
<!-- <RuntimeIdentifier>ios-arm64</RuntimeIdentifier>--> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath> |
|||
<DefineConstants>DEBUG</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<ConsolePause>false</ConsolePause> |
|||
<MtouchArch>x86_64</MtouchArch> |
|||
<MtouchLink>None</MtouchLink> |
|||
<MtouchDebug>True</MtouchDebug> |
|||
<MtouchSdkVersion>9.1</MtouchSdkVersion> |
|||
<MtouchProfiling>False</MtouchProfiling> |
|||
<MtouchFastDev>False</MtouchFastDev> |
|||
<MtouchNoSymbolStrip>False</MtouchNoSymbolStrip> |
|||
<MtouchUseLlvm>False</MtouchUseLlvm> |
|||
<MtouchUseThumb>False</MtouchUseThumb> |
|||
<MtouchEnableBitcode>False</MtouchEnableBitcode> |
|||
<MtouchUseSGen>False</MtouchUseSGen> |
|||
<MtouchUseRefCounting>False</MtouchUseRefCounting> |
|||
<OptimizePNGs>True</OptimizePNGs> |
|||
<MtouchTlsProvider>Default</MtouchTlsProvider> |
|||
<MtouchHttpClientHandler>HttpClientHandler</MtouchHttpClientHandler> |
|||
<MtouchFloat32>False</MtouchFloat32> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' "> |
|||
<DebugType>none</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\iPhoneSimulator\Release</OutputPath> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<MtouchLink>None</MtouchLink> |
|||
<MtouchArch>x86_64</MtouchArch> |
|||
<ConsolePause>false</ConsolePause> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\iPhone\Debug</OutputPath> |
|||
<DefineConstants>DEBUG</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<ConsolePause>false</ConsolePause> |
|||
<MtouchArch>ARMv7, ARM64</MtouchArch> |
|||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements> |
|||
<CodesignKey>iPhone Developer</CodesignKey> |
|||
<MtouchDebug>true</MtouchDebug> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' "> |
|||
<DebugType>none</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\iPhone\Release</OutputPath> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements> |
|||
<MtouchArch>ARMv7, ARM64</MtouchArch> |
|||
<ConsolePause>false</ConsolePause> |
|||
<CodesignKey>iPhone Developer</CodesignKey> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' "> |
|||
<DebugType>none</DebugType> |
|||
<Optimize>True</Optimize> |
|||
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<ConsolePause>False</ConsolePause> |
|||
<MtouchArch>ARMv7, ARM64</MtouchArch> |
|||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements> |
|||
<BuildIpa>True</BuildIpa> |
|||
<CodesignProvision>Automatic:AdHoc</CodesignProvision> |
|||
<CodesignKey>iPhone Distribution</CodesignKey> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' "> |
|||
<DebugType>none</DebugType> |
|||
<Optimize>True</Optimize> |
|||
<OutputPath>bin\iPhone\AppStore</OutputPath> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<ConsolePause>False</ConsolePause> |
|||
<MtouchArch>ARMv7, ARM64</MtouchArch> |
|||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements> |
|||
<CodesignProvision>Automatic:AppStore</CodesignProvision> |
|||
<CodesignKey>iPhone Distribution</CodesignKey> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Compile Include="Main.cs" /> |
|||
<Compile Include="AppDelegate.cs" /> |
|||
<None Include="Info.plist" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
<InterfaceDefinition Include="Resources\LaunchScreen.xib" /> |
|||
<Content Include="Entitlements.plist" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System" /> |
|||
<Reference Include="System.Xml" /> |
|||
<Reference Include="System.Core" /> |
|||
<Reference Include="Xamarin.iOS" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj"> |
|||
<Project>{4488AD85-1495-4809-9AA4-DDFE0A48527E}</Project> |
|||
<Name>Avalonia.iOS</Name> |
|||
<IsAppExtension>false</IsAppExtension> |
|||
<IsWatchApp>false</IsWatchApp> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj"> |
|||
<Project>{3E53A01A-B331-47F3-B828-4A5717E77A24}</Project> |
|||
<Name>Avalonia.Markup.Xaml</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj"> |
|||
<Project>{6417E941-21BC-467B-A771-0DE389353CE6}</Project> |
|||
<Name>Avalonia.Markup</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj"> |
|||
<Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project> |
|||
<Name>Avalonia.Animation</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj"> |
|||
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project> |
|||
<Name>Avalonia.Base</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj"> |
|||
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project> |
|||
<Name>Avalonia.Controls</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj"> |
|||
<Project>{7062AE20-5DCC-4442-9645-8195BDECE63E}</Project> |
|||
<Name>Avalonia.Diagnostics</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj"> |
|||
<Project>{62024B2D-53EB-4638-B26B-85EEAA54866E}</Project> |
|||
<Name>Avalonia.Input</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj"> |
|||
<Project>{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}</Project> |
|||
<Name>Avalonia.Interactivity</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj"> |
|||
<Project>{42472427-4774-4C81-8AFF-9F27B8E31721}</Project> |
|||
<Name>Avalonia.Layout</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj"> |
|||
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project> |
|||
<Name>Avalonia.Visuals</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj"> |
|||
<Project>{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}</Project> |
|||
<Name>Avalonia.Styling</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj"> |
|||
<Project>{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}</Project> |
|||
<Name>Avalonia.Themes.Default</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj"> |
|||
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project> |
|||
<Name>Avalonia.Skia</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj"> |
|||
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project> |
|||
<Name>ControlCatalog</Name> |
|||
</ProjectReference> |
|||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2-preview.33" /> |
|||
<ProjectReference Include="..\..\src\iOS\Avalonia.iOS\Avalonia.iOS.csproj" /> |
|||
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" /> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> |
|||
<Import Project="..\..\build\LegacyProject.targets" /> |
|||
<Import Project="..\..\build\SkiaSharp.props" /> |
|||
<Import Project="..\..\build\HarfBuzzSharp.props" /> |
|||
</Project> |
|||
|
|||
@ -1,36 +0,0 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("ControlCatalog.iOS")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("")] |
|||
[assembly: AssemblyProduct("ControlCatalog.iOS")] |
|||
[assembly: AssemblyCopyright("Copyright © 2016")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("57e0455d-d565-44bb-b069-ee1aa20f8337")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
@ -0,0 +1,23 @@ |
|||
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage" |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<StackPanel Orientation="Vertical" Spacing="4"> |
|||
<TextBlock Classes="h2">Example of ClipboardPage capabilities</TextBlock> |
|||
|
|||
<Button Click="CopyText" Content="Copy text to clipboard" /> |
|||
<Button Click="PasteText" Content="Paste text from clipboard" /> |
|||
<Button Click="CopyTextDataObject" Content="Copy text to clipboard (data object)" /> |
|||
<Button Click="PasteTextDataObject" Content="Paste text from clipboard (data object)" /> |
|||
|
|||
<Button Click="CopyFilesDataObject" Content="Copy files to clipboard (data object)" /> |
|||
<Button Click="PasteFilesDataObject" Content="Paste files from clipboard (data object)" /> |
|||
|
|||
<Button Click="GetFormats" Content="Get clipboard formats" /> |
|||
<Button Click="Clear" Content="Clear clipboard" /> |
|||
|
|||
<TextBox x:Name="ClipboardContent" |
|||
MinHeight="100" |
|||
AcceptsReturn="True" |
|||
Watermark="Text to copy of file names per line" /> |
|||
</StackPanel> |
|||
</UserControl> |
|||
@ -0,0 +1,77 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Input; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace ControlCatalog.Pages |
|||
{ |
|||
public partial class ClipboardPage : UserControl |
|||
{ |
|||
public ClipboardPage() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent"); |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private async void CopyText(object sender, RoutedEventArgs args) |
|||
{ |
|||
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text); |
|||
} |
|||
|
|||
private async void PasteText(object sender, RoutedEventArgs args) |
|||
{ |
|||
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync(); |
|||
} |
|||
|
|||
private async void CopyTextDataObject(object sender, RoutedEventArgs args) |
|||
{ |
|||
var dataObject = new DataObject(); |
|||
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty); |
|||
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); |
|||
} |
|||
|
|||
private async void PasteTextDataObject(object sender, RoutedEventArgs args) |
|||
{ |
|||
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty; |
|||
} |
|||
|
|||
private async void CopyFilesDataObject(object sender, RoutedEventArgs args) |
|||
{ |
|||
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); |
|||
if (files.Length == 0) |
|||
{ |
|||
return; |
|||
} |
|||
var dataObject = new DataObject(); |
|||
dataObject.Set(DataFormats.FileNames, files); |
|||
await Application.Current.Clipboard.SetDataObjectAsync(dataObject); |
|||
} |
|||
|
|||
private async void PasteFilesDataObject(object sender, RoutedEventArgs args) |
|||
{ |
|||
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>; |
|||
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty; |
|||
} |
|||
|
|||
private async void GetFormats(object sender, RoutedEventArgs args) |
|||
{ |
|||
var formats = await Application.Current.Clipboard.GetFormatsAsync(); |
|||
ClipboardContent.Text = string.Join(Environment.NewLine, formats); |
|||
} |
|||
|
|||
private async void Clear(object sender, RoutedEventArgs args) |
|||
{ |
|||
await Application.Current.Clipboard.ClearAsync(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
<Application xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
x:Class="IntegrationTestApp.App"> |
|||
<Application.Styles> |
|||
<FluentTheme Mode="Light"/> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,24 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace IntegrationTestApp |
|||
{ |
|||
public class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
public override void OnFrameworkInitializationCompleted() |
|||
{ |
|||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) |
|||
{ |
|||
desktop.MainWindow = new MainWindow(); |
|||
} |
|||
|
|||
base.OnFrameworkInitializationCompleted(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>WinExe</OutputType> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup> |
|||
<CFBundleName>IntegrationTestApp</CFBundleName> |
|||
<CFBundleIdentifier>net.avaloniaui.avalonia.integrationtestapp</CFBundleIdentifier> |
|||
<NSHighResolutionCapable>true</NSHighResolutionCapable> |
|||
<CFBundleShortVersionString>1.0.0</CFBundleShortVersionString> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Dotnet.Bundle" Version="0.9.13" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\BuildTargets.targets" /> |
|||
<Import Project="..\..\build\SampleApp.props" /> |
|||
<Import Project="..\..\build\ReferenceCoreLibraries.props" /> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,95 @@ |
|||
<Window 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="800" d:DesignHeight="450" |
|||
x:Class="IntegrationTestApp.MainWindow" |
|||
Title="IntegrationTestApp"> |
|||
<NativeMenu.Menu> |
|||
<NativeMenu> |
|||
<NativeMenuItem Header="File"> |
|||
<NativeMenu> |
|||
<NativeMenuItem Header="Open..."/> |
|||
</NativeMenu> |
|||
</NativeMenuItem> |
|||
<NativeMenuItem Header="View"> |
|||
<NativeMenu/> |
|||
</NativeMenuItem> |
|||
</NativeMenu> |
|||
</NativeMenu.Menu> |
|||
<DockPanel> |
|||
<NativeMenuBar DockPanel.Dock="Top"/> |
|||
|
|||
<TabControl TabStripPlacement="Left" Name="MainTabs"> |
|||
<TabItem Header="Automation"> |
|||
<StackPanel> |
|||
<TextBlock Name="TextBlockWithName">TextBlockWithName</TextBlock> |
|||
<TextBlock Name="NotTheAutomationId" AutomationProperties.AutomationId="TextBlockWithNameAndAutomationId"> |
|||
TextBlockWithNameAndAutomationId |
|||
</TextBlock> |
|||
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock> |
|||
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}"> |
|||
Foo |
|||
</TextBox> |
|||
</StackPanel> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="Button"> |
|||
<StackPanel> |
|||
<Button Name="DisabledButton" IsEnabled="False"> |
|||
Disabled Button |
|||
</Button> |
|||
<Button Name="BasicButton"> |
|||
Basic Button |
|||
</Button> |
|||
<Button Name="ButtonWithTextBlock"> |
|||
<TextBlock>Button with TextBlock</TextBlock> |
|||
</Button> |
|||
<Button Name="ButtonWithAcceleratorKey" HotKey="Ctrl+B">Button with Accelerator Key</Button> |
|||
</StackPanel> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="CheckBox"> |
|||
<StackPanel> |
|||
<CheckBox Name="UncheckedCheckBox">Unchecked</CheckBox> |
|||
<CheckBox Name="CheckedCheckBox" IsChecked="True">Checked</CheckBox> |
|||
<CheckBox Name="ThreeStateCheckBox" IsThreeState="True" IsChecked="{x:Null}">ThreeState</CheckBox> |
|||
</StackPanel> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="ComboBox"> |
|||
<StackPanel> |
|||
<ComboBox Name="BasicComboBox"> |
|||
<ComboBoxItem>Item 0</ComboBoxItem> |
|||
<ComboBoxItem>Item 1</ComboBoxItem> |
|||
</ComboBox> |
|||
<Button Name="ComboBoxSelectionClear">Clear Selection</Button> |
|||
<Button Name="ComboBoxSelectFirst">Select First</Button> |
|||
</StackPanel> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="ListBox"> |
|||
<DockPanel> |
|||
<StackPanel DockPanel.Dock="Bottom"> |
|||
<Button Name="ListBoxSelectionClear">Clear Selection</Button> |
|||
</StackPanel> |
|||
<ListBox Name="BasicListBox" Items="{Binding ListBoxItems}" SelectionMode="Multiple"/> |
|||
</DockPanel> |
|||
</TabItem> |
|||
|
|||
<TabItem Header="Menu"> |
|||
<DockPanel> |
|||
<Menu DockPanel.Dock="Top"> |
|||
<MenuItem Name="RootMenuItem" Header="_Root"> |
|||
<MenuItem Name="Child1MenuItem" Header="_Child 1" InputGesture="Ctrl+O" Click="MenuClicked"/> |
|||
<MenuItem Name="Child2MenuItem" Header="_Child 1"> |
|||
<MenuItem Name="GrandchildMenuItem" Header="_Grandchild" Click="MenuClicked"/> |
|||
</MenuItem> |
|||
</MenuItem> |
|||
</Menu> |
|||
<TextBlock Name="ClickedMenuItem">None</TextBlock> |
|||
</DockPanel> |
|||
</TabItem> |
|||
</TabControl> |
|||
</DockPanel> |
|||
</Window> |
|||
@ -0,0 +1,67 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace IntegrationTestApp |
|||
{ |
|||
public class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
InitializeViewMenu(); |
|||
this.AttachDevTools(); |
|||
AddHandler(Button.ClickEvent, OnButtonClick); |
|||
ListBoxItems = Enumerable.Range(0, 100).Select(x => "Item " + x).ToList(); |
|||
DataContext = this; |
|||
} |
|||
|
|||
public List<string> ListBoxItems { get; } |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
private void InitializeViewMenu() |
|||
{ |
|||
var mainTabs = this.FindControl<TabControl>("MainTabs"); |
|||
var viewMenu = (NativeMenuItem)NativeMenu.GetMenu(this).Items[1]; |
|||
|
|||
foreach (TabItem tabItem in mainTabs.Items) |
|||
{ |
|||
var menuItem = new NativeMenuItem |
|||
{ |
|||
Header = (string)tabItem.Header!, |
|||
IsChecked = tabItem.IsSelected, |
|||
ToggleType = NativeMenuItemToggleType.Radio, |
|||
}; |
|||
|
|||
menuItem.Click += (s, e) => tabItem.IsSelected = true; |
|||
viewMenu.Menu.Items.Add(menuItem); |
|||
} |
|||
} |
|||
|
|||
private void MenuClicked(object? sender, RoutedEventArgs e) |
|||
{ |
|||
var clickedMenuItemTextBlock = this.FindControl<TextBlock>("ClickedMenuItem"); |
|||
clickedMenuItemTextBlock.Text = ((MenuItem)sender!).Header.ToString(); |
|||
} |
|||
|
|||
private void OnButtonClick(object? sender, RoutedEventArgs e) |
|||
{ |
|||
var source = e.Source as Button; |
|||
|
|||
if (source?.Name == "ComboBoxSelectionClear") |
|||
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = -1; |
|||
if (source?.Name == "ComboBoxSelectFirst") |
|||
this.FindControl<ComboBox>("BasicComboBox").SelectedIndex = 0; |
|||
if (source?.Name == "ListBoxSelectionClear") |
|||
this.FindControl<ListBox>("BasicListBox").SelectedIndex = -1; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
|
|||
namespace IntegrationTestApp |
|||
{ |
|||
class Program |
|||
{ |
|||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
|||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
|||
// yet and stuff might break.
|
|||
public static void Main(string[] args) => BuildAvaloniaApp() |
|||
.StartWithClassicDesktopLifetime(args); |
|||
|
|||
// Avalonia configuration, don't remove; also used by visual designer.
|
|||
public static AppBuilder BuildAvaloniaApp() |
|||
=> AppBuilder.Configure<App>() |
|||
.UsePlatformDetect() |
|||
.LogToTrace(); |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
cd $(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) |
|||
dotnet restore -r osx-arm64 |
|||
dotnet msbuild -t:BundleApp -p:RuntimeIdentifier=osx-arm64 -p:_AvaloniaUseExternalMSBuild=false |
|||
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<!-- |
|||
To use the Avalonia CI feed to get unstable packages, move this file to the root of your solution. |
|||
--> |
|||
|
|||
<configuration> |
|||
<packageSources> |
|||
<add key="AvaloniaCI" value="https://www.myget.org/F/avalonia-ci/api/v2" /> |
|||
</packageSources> |
|||
</configuration> |
|||
@ -0,0 +1,71 @@ |
|||
<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="800" d:DesignHeight="450" |
|||
x:Class="RenderDemo.Pages.BrushesPage"> |
|||
<Canvas Background="White" Width="480" Height="360"> |
|||
<Rectangle Canvas.Left="20" Canvas.Top="20" Width="440" Height="50"> |
|||
<Rectangle.Fill> |
|||
<LinearGradientBrush StartPoint="0,0" EndPoint="410,0"> |
|||
<LinearGradientBrush.Transform> |
|||
<TransformGroup> |
|||
<ScaleTransform ScaleX="0.5" /> |
|||
<SkewTransform /> |
|||
<RotateTransform /> |
|||
<TranslateTransform X="5" Y="15" /> |
|||
</TransformGroup> |
|||
</LinearGradientBrush.Transform> |
|||
<LinearGradientBrush.GradientStops> |
|||
<GradientStop Color="Blue" Offset="0" /> |
|||
<GradientStop Color="Green" Offset="0.5" /> |
|||
<GradientStop Color="Lime" Offset="1" /> |
|||
</LinearGradientBrush.GradientStops> |
|||
</LinearGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
<TextBlock Canvas.Left="20" Canvas.Top="70" FontSize="30" Text="scale(0.5) on gradient" /> |
|||
<Rectangle Canvas.Left="20" Canvas.Top="110" Width="440" Height="50"> |
|||
<Rectangle.Fill> |
|||
<RadialGradientBrush Center="0.0,0.0" GradientOrigin="0.0,0.0" Radius="0.13636364"> |
|||
<RadialGradientBrush.Transform> |
|||
<TransformGroup> |
|||
<ScaleTransform /> |
|||
<SkewTransform AngleX="45" /> |
|||
<RotateTransform /> |
|||
<TranslateTransform X="240" Y="45" /> |
|||
</TransformGroup> |
|||
</RadialGradientBrush.Transform> |
|||
<RadialGradientBrush.GradientStops> |
|||
<GradientStop Color="Black" Offset="0" /> |
|||
<GradientStop Color="#FFFFA500" Offset="1" /> |
|||
</RadialGradientBrush.GradientStops> |
|||
</RadialGradientBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
<TextBlock Canvas.Left="20" Canvas.Top="160" FontSize="30" Text="skewX(45) on gradient" /> |
|||
<Rectangle Canvas.Left="20" Canvas.Top="210" Width="440" Height="50"> |
|||
<Rectangle.Fill> |
|||
<VisualBrush TileMode="Tile" SourceRect="0,0,20,20" DestinationRect="0,0,20,20" Stretch="None"> |
|||
<VisualBrush.Transform> |
|||
<TransformGroup> |
|||
<ScaleTransform ScaleX="2" ScaleY="2" /> |
|||
<SkewTransform AngleX="45" /> |
|||
<RotateTransform /> |
|||
<TranslateTransform X="5" Y="5" /> |
|||
</TransformGroup> |
|||
</VisualBrush.Transform> |
|||
<VisualBrush.Visual> |
|||
<Canvas Width="20" Height="20"> |
|||
<Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="10" Fill="Maroon" /> |
|||
<Rectangle Canvas.Left="10" Canvas.Top="0" Width="10" Height="10" Fill="Green" /> |
|||
<Rectangle Canvas.Left="0" Canvas.Top="10" Width="10" Height="10" Fill="Blue" /> |
|||
<Rectangle Canvas.Left="10" Canvas.Top="10" Width="10" Height="10" Fill="Yellow" /> |
|||
</Canvas> |
|||
</VisualBrush.Visual> |
|||
</VisualBrush> |
|||
</Rectangle.Fill> |
|||
</Rectangle> |
|||
<TextBlock Canvas.Left="20" Canvas.Top="260" FontSize="30" Text="scale(2), skewX(45) on pattern" /> |
|||
</Canvas> |
|||
</UserControl> |
|||
@ -0,0 +1,18 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace RenderDemo.Pages; |
|||
|
|||
public class BrushesPage : UserControl |
|||
{ |
|||
public BrushesPage() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
|
|||
@ -1,12 +1,19 @@ |
|||
<Project Sdk="Xamarin.Legacy.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks> |
|||
<TargetFrameworks>net6.0-android</TargetFrameworks> |
|||
<TargetFrameworks Condition="'$(MSBuildRuntimeType)' != 'Core'">$(TargetFrameworks);monoandroid11.0</TargetFrameworks> |
|||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver> |
|||
<DebugType>portable</DebugType> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" /> |
|||
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" /> |
|||
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" /> |
|||
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
|
|||
@ -1,35 +1,82 @@ |
|||
using Android.App; |
|||
using Android.OS; |
|||
using Android.Views; |
|||
using AndroidX.AppCompat.App; |
|||
using Android.Content.Res; |
|||
using AndroidX.Lifecycle; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Android |
|||
{ |
|||
public abstract class AvaloniaActivity : Activity |
|||
public abstract class AvaloniaActivity<TApp> : AppCompatActivity where TApp : Application, new() |
|||
{ |
|||
internal class SingleViewLifetime : ISingleViewApplicationLifetime |
|||
{ |
|||
public AvaloniaView View { get; internal set; } |
|||
|
|||
public Control MainView |
|||
{ |
|||
get => (Control)View.Content; |
|||
set => View.Content = value; |
|||
} |
|||
} |
|||
|
|||
internal AvaloniaView View; |
|||
object _content; |
|||
internal AvaloniaViewModel _viewModel; |
|||
|
|||
protected virtual AppBuilder CustomizeAppBuilder(AppBuilder builder) => builder.UseAndroid(); |
|||
|
|||
protected override void OnCreate(Bundle savedInstanceState) |
|||
{ |
|||
var builder = AppBuilder.Configure<TApp>(); |
|||
|
|||
CustomizeAppBuilder(builder); |
|||
|
|||
View = new AvaloniaView(this); |
|||
if (_content != null) |
|||
View.Content = _content; |
|||
SetContentView(View); |
|||
|
|||
var lifetime = new SingleViewLifetime(); |
|||
lifetime.View = View; |
|||
|
|||
builder.AfterSetup(x => |
|||
{ |
|||
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel; |
|||
|
|||
if (_viewModel.Content != null) |
|||
{ |
|||
View.Content = _viewModel.Content; |
|||
} |
|||
|
|||
View.Prepare(); |
|||
}); |
|||
|
|||
builder.SetupWithLifetime(lifetime); |
|||
|
|||
base.OnCreate(savedInstanceState); |
|||
} |
|||
|
|||
public object Content |
|||
{ |
|||
get |
|||
{ |
|||
return _content; |
|||
return _viewModel.Content; |
|||
} |
|||
set |
|||
{ |
|||
_content = value; |
|||
_viewModel.Content = value; |
|||
if (View != null) |
|||
View.Content = value; |
|||
} |
|||
} |
|||
|
|||
public override void OnConfigurationChanged(Configuration newConfig) |
|||
{ |
|||
base.OnConfigurationChanged(newConfig); |
|||
} |
|||
|
|||
protected override void OnDestroy() |
|||
{ |
|||
View.Content = null; |
|||
|
|||
base.OnDestroy(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
|
|||
namespace Avalonia.Android |
|||
{ |
|||
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel |
|||
{ |
|||
public object Content { get; set; } |
|||
} |
|||
} |
|||
@ -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. |
|||
@ -1,6 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<resources> |
|||
<string name="Hello">Hello World, Click Me!</string> |
|||
<string name="ApplicationName">$projectname$</string> |
|||
</resources> |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Metadata |
|||
{ |
|||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] |
|||
public class TrimSurroundingWhitespaceAttribute : Attribute |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Metadata |
|||
{ |
|||
/// <summary>
|
|||
/// Indicates that a collection type should be processed as being whitespace significant by a XAML processor.
|
|||
/// </summary>
|
|||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] |
|||
public class WhitespaceSignificantCollectionAttribute : Attribute |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
Total Issues: 0 |
|||
@ -0,0 +1,28 @@ |
|||
using Avalonia.Automation.Peers; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Contains values used as automation property identifiers by UI Automation providers.
|
|||
/// </summary>
|
|||
public static class AutomationElementIdentifiers |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies the bounding rectangle automation property. The bounding rectangle property
|
|||
/// value is returned by the <see cref="AutomationPeer.GetBoundingRectangle"/> method.
|
|||
/// </summary>
|
|||
public static AutomationProperty BoundingRectangleProperty { get; } = new AutomationProperty(); |
|||
|
|||
/// <summary>
|
|||
/// Identifies the class name automation property. The class name property value is returned
|
|||
/// by the <see cref="AutomationPeer.GetClassName"/> method.
|
|||
/// </summary>
|
|||
public static AutomationProperty ClassNameProperty { get; } = new AutomationProperty(); |
|||
|
|||
/// <summary>
|
|||
/// Identifies the name automation property. The class name property value is returned
|
|||
/// by the <see cref="AutomationPeer.GetName"/> method.
|
|||
/// </summary>
|
|||
public static AutomationProperty NameProperty { get; } = new AutomationProperty(); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Describes the notification characteristics of a particular live region
|
|||
/// </summary>
|
|||
public enum AutomationLiveSetting |
|||
{ |
|||
/// <summary>
|
|||
/// The element does not send notifications if the content of the live region has changed.
|
|||
/// </summary>
|
|||
Off = 0, |
|||
|
|||
/// <summary>
|
|||
/// The element sends non-interruptive notifications if the content of the live region has
|
|||
/// changed. With this setting, UI Automation clients and assistive technologies are expected
|
|||
/// to not interrupt the user to inform of changes to the live region.
|
|||
/// </summary>
|
|||
Polite = 1, |
|||
|
|||
/// <summary>
|
|||
/// The element sends interruptive notifications if the content of the live region has changed.
|
|||
/// With this setting, UI Automation clients and assistive technologies are expected to interrupt
|
|||
/// the user to inform of changes to the live region.
|
|||
/// </summary>
|
|||
Assertive = 2, |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,630 @@ |
|||
using System; |
|||
using Avalonia.Automation.Peers; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Declares how a control should included in different views of the automation tree.
|
|||
/// </summary>
|
|||
public enum AccessibilityView |
|||
{ |
|||
/// <summary>
|
|||
/// The control is included in the Raw view of the automation tree.
|
|||
/// </summary>
|
|||
Raw, |
|||
|
|||
/// <summary>
|
|||
/// The control is included in the Control view of the automation tree.
|
|||
/// </summary>
|
|||
Control, |
|||
|
|||
/// <summary>
|
|||
/// The control is included in the Content view of the automation tree.
|
|||
/// </summary>
|
|||
Content, |
|||
} |
|||
|
|||
public static class AutomationProperties |
|||
{ |
|||
internal const int AutomationPositionInSetDefault = -1; |
|||
internal const int AutomationSizeOfSetDefault = -1; |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.AcceleratorKey attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> AcceleratorKeyProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"AcceleratorKey", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.AccessibilityView attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<AccessibilityView> AccessibilityViewProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, AccessibilityView>( |
|||
"AccessibilityView", |
|||
typeof(AutomationProperties), |
|||
defaultValue: AccessibilityView.Content); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.AccessKey attached property
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> AccessKeyProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"AccessKey", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.AutomationId attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> AutomationIdProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"AutomationId", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.ControlTypeOverride attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<AutomationControlType?> ControlTypeOverrideProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, AutomationControlType?>( |
|||
"ControlTypeOverride", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.HelpText attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> HelpTextProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"HelpText", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.IsColumnHeader attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<bool> IsColumnHeaderProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, bool>( |
|||
"IsColumnHeader", |
|||
typeof(AutomationProperties), |
|||
false); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.IsRequiredForForm attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<bool> IsRequiredForFormProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, bool>( |
|||
"IsRequiredForForm", |
|||
typeof(AutomationProperties), |
|||
false); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.IsRowHeader attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<bool> IsRowHeaderProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, bool>( |
|||
"IsRowHeader", |
|||
typeof(AutomationProperties), |
|||
false); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.IsOffscreenBehavior attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<IsOffscreenBehavior> IsOffscreenBehaviorProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, IsOffscreenBehavior>( |
|||
"IsOffscreenBehavior", |
|||
typeof(AutomationProperties), |
|||
IsOffscreenBehavior.Default); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.ItemStatus attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> ItemStatusProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"ItemStatus", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.ItemType attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> ItemTypeProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"ItemType", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.LabeledBy attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<IControl> LabeledByProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, IControl>( |
|||
"LabeledBy", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.LiveSetting attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<AutomationLiveSetting> LiveSettingProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, AutomationLiveSetting>( |
|||
"LiveSetting", |
|||
typeof(AutomationProperties), |
|||
AutomationLiveSetting.Off); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.Name attached attached property.
|
|||
/// </summary>
|
|||
public static readonly AttachedProperty<string> NameProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, string>( |
|||
"Name", |
|||
typeof(AutomationProperties)); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.PositionInSet attached property.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The PositionInSet property describes the ordinal location of the element within a set
|
|||
/// of elements which are considered to be siblings. PositionInSet works in coordination
|
|||
/// with the SizeOfSet property to describe the ordinal location in the set.
|
|||
/// </remarks>
|
|||
public static readonly AttachedProperty<int> PositionInSetProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, int>( |
|||
"PositionInSet", |
|||
typeof(AutomationProperties), |
|||
AutomationPositionInSetDefault); |
|||
|
|||
/// <summary>
|
|||
/// Defines the AutomationProperties.SizeOfSet attached property.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// The SizeOfSet property describes the count of automation elements in a group or set
|
|||
/// that are considered to be siblings. SizeOfSet works in coordination with the PositionInSet
|
|||
/// property to describe the count of items in the set.
|
|||
/// </remarks>
|
|||
public static readonly AttachedProperty<int> SizeOfSetProperty = |
|||
AvaloniaProperty.RegisterAttached<StyledElement, int>( |
|||
"SizeOfSet", |
|||
typeof(AutomationProperties), |
|||
AutomationSizeOfSetDefault); |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting AcceleratorKey property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetAcceleratorKey(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(AcceleratorKeyProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading AcceleratorKey property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetAcceleratorKey(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(AcceleratorKeyProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting AccessibilityView property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetAccessibilityView(StyledElement element, AccessibilityView value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(AccessibilityViewProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading AccessibilityView property from a StyledElement.
|
|||
/// </summary>
|
|||
public static AccessibilityView GetAccessibilityView(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return element.GetValue(AccessibilityViewProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting AccessKey property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetAccessKey(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(AccessKeyProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading AccessKey property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetAccessKey(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(AccessKeyProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting AutomationId property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetAutomationId(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(AutomationIdProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading AutomationId property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetAutomationId(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return element.GetValue(AutomationIdProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting ControlTypeOverride property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetControlTypeOverride(StyledElement element, AutomationControlType? value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(ControlTypeOverrideProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading ControlTypeOverride property from a StyledElement.
|
|||
/// </summary>
|
|||
public static AutomationControlType? GetControlTypeOverride(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return element.GetValue(ControlTypeOverrideProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting HelpText property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetHelpText(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(HelpTextProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading HelpText property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetHelpText(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(HelpTextProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting IsColumnHeader property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetIsColumnHeader(StyledElement element, bool value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(IsColumnHeaderProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading IsColumnHeader property from a StyledElement.
|
|||
/// </summary>
|
|||
public static bool GetIsColumnHeader(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((bool)element.GetValue(IsColumnHeaderProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting IsRequiredForForm property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetIsRequiredForForm(StyledElement element, bool value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(IsRequiredForFormProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading IsRequiredForForm property from a StyledElement.
|
|||
/// </summary>
|
|||
public static bool GetIsRequiredForForm(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((bool)element.GetValue(IsRequiredForFormProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading IsRowHeader property from a StyledElement.
|
|||
/// </summary>
|
|||
public static bool GetIsRowHeader(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((bool)element.GetValue(IsRowHeaderProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting IsRowHeader property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetIsRowHeader(StyledElement element, bool value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(IsRowHeaderProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting IsOffscreenBehavior property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetIsOffscreenBehavior(StyledElement element, IsOffscreenBehavior value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(IsOffscreenBehaviorProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading IsOffscreenBehavior property from a StyledElement.
|
|||
/// </summary>
|
|||
public static IsOffscreenBehavior GetIsOffscreenBehavior(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((IsOffscreenBehavior)element.GetValue(IsOffscreenBehaviorProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting ItemStatus property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetItemStatus(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(ItemStatusProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading ItemStatus property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetItemStatus(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(ItemStatusProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting ItemType property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetItemType(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(ItemTypeProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading ItemType property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetItemType(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(ItemTypeProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting LabeledBy property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetLabeledBy(StyledElement element, IControl value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(LabeledByProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading LabeledBy property from a StyledElement.
|
|||
/// </summary>
|
|||
public static IControl GetLabeledBy(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return element.GetValue(LabeledByProperty); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting LiveSetting property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetLiveSetting(StyledElement element, AutomationLiveSetting value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(LiveSettingProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading LiveSetting property from a StyledElement.
|
|||
/// </summary>
|
|||
public static AutomationLiveSetting GetLiveSetting(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((AutomationLiveSetting)element.GetValue(LiveSettingProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting Name property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetName(StyledElement element, string value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(NameProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading Name property from a StyledElement.
|
|||
/// </summary>
|
|||
public static string GetName(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((string)element.GetValue(NameProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting PositionInSet property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetPositionInSet(StyledElement element, int value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(PositionInSetProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading PositionInSet property from a StyledElement.
|
|||
/// </summary>
|
|||
public static int GetPositionInSet(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((int)element.GetValue(PositionInSetProperty)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for setting SizeOfSet property on a StyledElement.
|
|||
/// </summary>
|
|||
public static void SetSizeOfSet(StyledElement element, int value) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
element.SetValue(SizeOfSetProperty, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Helper for reading SizeOfSet property from a StyledElement.
|
|||
/// </summary>
|
|||
public static int GetSizeOfSet(StyledElement element) |
|||
{ |
|||
if (element == null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(element)); |
|||
} |
|||
|
|||
return ((int)element.GetValue(SizeOfSetProperty)); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,11 @@ |
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies a property of <see cref="AutomationElementIdentifiers"/> or of a specific
|
|||
/// control pattern.
|
|||
/// </summary>
|
|||
public sealed class AutomationProperty |
|||
{ |
|||
internal AutomationProperty() { } |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
public class AutomationPropertyChangedEventArgs : EventArgs |
|||
{ |
|||
public AutomationPropertyChangedEventArgs( |
|||
AutomationProperty property, |
|||
object? oldValue, |
|||
object? newValue) |
|||
{ |
|||
Property = property; |
|||
OldValue = oldValue; |
|||
NewValue = newValue; |
|||
} |
|||
|
|||
public AutomationProperty Property { get; } |
|||
public object? OldValue { get; } |
|||
public object? NewValue { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
public class ElementNotEnabledException : Exception |
|||
{ |
|||
public ElementNotEnabledException() : base("Element not enabled.") { } |
|||
public ElementNotEnabledException(string message) : base(message) { } |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using Avalonia.Automation.Provider; |
|||
|
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Contains values used as identifiers by <see cref="IExpandCollapseProvider"/>.
|
|||
/// </summary>
|
|||
public static class ExpandCollapsePatternIdentifiers |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies <see cref="IExpandCollapseProvider.ExpandCollapseState"/> automation property.
|
|||
/// </summary>
|
|||
public static AutomationProperty ExpandCollapseStateProperty { get; } = new AutomationProperty(); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// Contains values that specify the <see cref="ExpandCollapseState"/> of a UI Automation element.
|
|||
/// </summary>
|
|||
public enum ExpandCollapseState |
|||
{ |
|||
/// <summary>
|
|||
/// No child nodes, controls, or content of the UI Automation element are displayed.
|
|||
/// </summary>
|
|||
Collapsed, |
|||
|
|||
/// <summary>
|
|||
/// All child nodes, controls or content of the UI Automation element are displayed.
|
|||
/// </summary>
|
|||
Expanded, |
|||
|
|||
/// <summary>
|
|||
/// The UI Automation element has no child nodes, controls, or content to display.
|
|||
/// </summary>
|
|||
LeafNode, |
|||
|
|||
/// <summary>
|
|||
/// Some, but not all, child nodes, controls, or content of the UI Automation element are
|
|||
/// displayed.
|
|||
/// </summary>
|
|||
PartiallyExpanded |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
namespace Avalonia.Automation |
|||
{ |
|||
/// <summary>
|
|||
/// This enum offers different ways of evaluating the IsOffscreen AutomationProperty
|
|||
/// </summary>
|
|||
public enum IsOffscreenBehavior |
|||
{ |
|||
/// <summary>
|
|||
/// The AutomationProperty IsOffscreen is calculated based on IsVisible.
|
|||
/// </summary>
|
|||
Default, |
|||
/// <summary>
|
|||
/// The AutomationProperty IsOffscreen is false.
|
|||
/// </summary>
|
|||
Onscreen, |
|||
/// <summary>
|
|||
/// The AutomationProperty IsOffscreen if true.
|
|||
/// </summary>
|
|||
Offscreen, |
|||
/// <summary>
|
|||
/// The AutomationProperty IsOffscreen is calculated based on clip regions.
|
|||
/// </summary>
|
|||
FromClip, |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,263 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public enum AutomationControlType |
|||
{ |
|||
None, |
|||
Button, |
|||
Calendar, |
|||
CheckBox, |
|||
ComboBox, |
|||
ComboBoxItem, |
|||
Edit, |
|||
Hyperlink, |
|||
Image, |
|||
ListItem, |
|||
List, |
|||
Menu, |
|||
MenuBar, |
|||
MenuItem, |
|||
ProgressBar, |
|||
RadioButton, |
|||
ScrollBar, |
|||
Slider, |
|||
Spinner, |
|||
StatusBar, |
|||
Tab, |
|||
TabItem, |
|||
Text, |
|||
ToolBar, |
|||
ToolTip, |
|||
Tree, |
|||
TreeItem, |
|||
Custom, |
|||
Group, |
|||
Thumb, |
|||
DataGrid, |
|||
DataItem, |
|||
Document, |
|||
SplitButton, |
|||
Window, |
|||
Pane, |
|||
Header, |
|||
HeaderItem, |
|||
Table, |
|||
TitleBar, |
|||
Separator, |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Provides a base class that exposes an element to UI Automation.
|
|||
/// </summary>
|
|||
public abstract class AutomationPeer |
|||
{ |
|||
/// <summary>
|
|||
/// Attempts to bring the element associated with the automation peer into view.
|
|||
/// </summary>
|
|||
public void BringIntoView() => BringIntoViewCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the accelerator key combinations for the element that is associated with the UI
|
|||
/// Automation peer.
|
|||
/// </summary>
|
|||
public string? GetAcceleratorKey() => GetAcceleratorKeyCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the access key for the element that is associated with the automation peer.
|
|||
/// </summary>
|
|||
public string? GetAccessKey() => GetAccessKeyCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the control type for the element that is associated with the UI Automation peer.
|
|||
/// </summary>
|
|||
public AutomationControlType GetAutomationControlType() => GetControlTypeOverrideCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the automation ID of the element that is associated with the UI Automation peer.
|
|||
/// </summary>
|
|||
public string? GetAutomationId() => GetAutomationIdCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the bounding rectangle of the element that is associated with the automation peer
|
|||
/// in top-level coordinates.
|
|||
/// </summary>
|
|||
public Rect GetBoundingRectangle() => GetBoundingRectangleCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets the child automation peers.
|
|||
/// </summary>
|
|||
public IReadOnlyList<AutomationPeer> GetChildren() => GetOrCreateChildrenCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a string that describes the class of the element.
|
|||
/// </summary>
|
|||
public string GetClassName() => GetClassNameCore() ?? string.Empty; |
|||
|
|||
/// <summary>
|
|||
/// Gets the automation peer for the label that is targeted to the element.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public AutomationPeer? GetLabeledBy() => GetLabeledByCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a human-readable localized string that represents the type of the control that is
|
|||
/// associated with this automation peer.
|
|||
/// </summary>
|
|||
public string GetLocalizedControlType() => GetLocalizedControlTypeCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets text that describes the element that is associated with this automation peer.
|
|||
/// </summary>
|
|||
public string GetName() => GetNameCore() ?? string.Empty; |
|||
|
|||
/// <summary>
|
|||
/// Gets the <see cref="AutomationPeer"/> that is the parent of this <see cref="AutomationPeer"/>.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public AutomationPeer? GetParent() => GetParentCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the element that is associated with this automation
|
|||
/// peer currently has keyboard focus.
|
|||
/// </summary>
|
|||
public bool HasKeyboardFocus() => HasKeyboardFocusCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the element that is associated with this automation
|
|||
/// peer contains data that is presented to the user.
|
|||
/// </summary>
|
|||
public bool IsContentElement() => IsControlElement() && IsContentElementCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the element is understood by the user as
|
|||
/// interactive or as contributing to the logical structure of the control in the GUI.
|
|||
/// </summary>
|
|||
public bool IsControlElement() => IsControlElementCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the control is enabled for user interaction.
|
|||
/// </summary>
|
|||
public bool IsEnabled() => IsEnabledCore(); |
|||
|
|||
/// <summary>
|
|||
/// Gets a value that indicates whether the element can accept keyboard focus.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public bool IsKeyboardFocusable() => IsKeyboardFocusableCore(); |
|||
|
|||
/// <summary>
|
|||
/// Sets the keyboard focus on the element that is associated with this automation peer.
|
|||
/// </summary>
|
|||
public void SetFocus() => SetFocusCore(); |
|||
|
|||
/// <summary>
|
|||
/// Shows the context menu for the element that is associated with this automation peer.
|
|||
/// </summary>
|
|||
/// <returns>true if a context menu is present for the element; otherwise false.</returns>
|
|||
public bool ShowContextMenu() => ShowContextMenuCore(); |
|||
|
|||
/// <summary>
|
|||
/// Tries to get a provider of the specified type from the peer.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The provider type.</typeparam>
|
|||
/// <returns>The provider, or null if not implemented on this peer.</returns>
|
|||
public T? GetProvider<T>() => (T?)GetProviderCore(typeof(T)); |
|||
|
|||
/// <summary>
|
|||
/// Occurs when the children of the automation peer have changed.
|
|||
/// </summary>
|
|||
public event EventHandler? ChildrenChanged; |
|||
|
|||
/// <summary>
|
|||
/// Occurs when a property value of the automation peer has changed.
|
|||
/// </summary>
|
|||
public event EventHandler<AutomationPropertyChangedEventArgs>? PropertyChanged; |
|||
|
|||
/// <summary>
|
|||
/// Raises an event to notify the automation client the the children of the peer have changed.
|
|||
/// </summary>
|
|||
protected void RaiseChildrenChangedEvent() => ChildrenChanged?.Invoke(this, EventArgs.Empty); |
|||
|
|||
/// <summary>
|
|||
/// Raises an event to notify the automation client of a changed property value.
|
|||
/// </summary>
|
|||
/// <param name="property">The property that changed.</param>
|
|||
/// <param name="oldValue">The previous value of the property.</param>
|
|||
/// <param name="newValue">The new value of the property.</param>
|
|||
public void RaisePropertyChangedEvent( |
|||
AutomationProperty property, |
|||
object? oldValue, |
|||
object? newValue) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new AutomationPropertyChangedEventArgs(property, oldValue, newValue)); |
|||
} |
|||
|
|||
protected virtual string GetLocalizedControlTypeCore() |
|||
{ |
|||
var controlType = GetAutomationControlType(); |
|||
|
|||
return controlType switch |
|||
{ |
|||
AutomationControlType.CheckBox => "check box", |
|||
AutomationControlType.ComboBox => "combo box", |
|||
AutomationControlType.ListItem => "list item", |
|||
AutomationControlType.MenuBar => "menu bar", |
|||
AutomationControlType.MenuItem => "menu item", |
|||
AutomationControlType.ProgressBar => "progress bar", |
|||
AutomationControlType.RadioButton => "radio button", |
|||
AutomationControlType.ScrollBar => "scroll bar", |
|||
AutomationControlType.StatusBar => "status bar", |
|||
AutomationControlType.TabItem => "tab item", |
|||
AutomationControlType.ToolBar => "toolbar", |
|||
AutomationControlType.ToolTip => "tooltip", |
|||
AutomationControlType.TreeItem => "tree item", |
|||
AutomationControlType.Custom => "custom", |
|||
AutomationControlType.DataGrid => "data grid", |
|||
AutomationControlType.DataItem => "data item", |
|||
AutomationControlType.SplitButton => "split button", |
|||
AutomationControlType.HeaderItem => "header item", |
|||
AutomationControlType.TitleBar => "title bar", |
|||
_ => controlType.ToString().ToLowerInvariant(), |
|||
}; |
|||
} |
|||
|
|||
protected abstract void BringIntoViewCore(); |
|||
protected abstract string? GetAcceleratorKeyCore(); |
|||
protected abstract string? GetAccessKeyCore(); |
|||
protected abstract AutomationControlType GetAutomationControlTypeCore(); |
|||
protected abstract string? GetAutomationIdCore(); |
|||
protected abstract Rect GetBoundingRectangleCore(); |
|||
protected abstract IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore(); |
|||
protected abstract string GetClassNameCore(); |
|||
protected abstract AutomationPeer? GetLabeledByCore(); |
|||
protected abstract string? GetNameCore(); |
|||
protected abstract AutomationPeer? GetParentCore(); |
|||
protected abstract bool HasKeyboardFocusCore(); |
|||
protected abstract bool IsContentElementCore(); |
|||
protected abstract bool IsControlElementCore(); |
|||
protected abstract bool IsEnabledCore(); |
|||
protected abstract bool IsKeyboardFocusableCore(); |
|||
protected abstract void SetFocusCore(); |
|||
protected abstract bool ShowContextMenuCore(); |
|||
|
|||
protected virtual AutomationControlType GetControlTypeOverrideCore() |
|||
{ |
|||
return GetAutomationControlTypeCore(); |
|||
} |
|||
|
|||
protected virtual object? GetProviderCore(Type providerType) |
|||
{ |
|||
return providerType.IsAssignableFrom(this.GetType()) ? this : null; |
|||
} |
|||
|
|||
protected internal abstract bool TrySetParent(AutomationPeer? parent); |
|||
|
|||
protected void EnsureEnabled() |
|||
{ |
|||
if (!IsEnabled()) |
|||
throw new ElementNotEnabledException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ButtonAutomationPeer : ContentControlAutomationPeer, |
|||
IInvokeProvider |
|||
{ |
|||
public ButtonAutomationPeer(Button owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new Button Owner => (Button)base.Owner; |
|||
|
|||
public void Invoke() |
|||
{ |
|||
EnsureEnabled(); |
|||
(Owner as Button)?.PerformClick(); |
|||
} |
|||
|
|||
protected override string? GetAcceleratorKeyCore() |
|||
{ |
|||
var result = base.GetAcceleratorKeyCore(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(result)) |
|||
{ |
|||
result = Owner.HotKey?.ToString(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.Button; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => true; |
|||
protected override bool IsControlElementCore() => true; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,137 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ComboBoxAutomationPeer : SelectingItemsControlAutomationPeer, |
|||
IExpandCollapseProvider, |
|||
IValueProvider |
|||
{ |
|||
private UnrealizedSelectionPeer[]? _selection; |
|||
|
|||
public ComboBoxAutomationPeer(ComboBox owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new ComboBox Owner => (ComboBox)base.Owner; |
|||
|
|||
public ExpandCollapseState ExpandCollapseState => ToState(Owner.IsDropDownOpen); |
|||
public bool ShowsMenu => true; |
|||
public void Collapse() => Owner.IsDropDownOpen = false; |
|||
public void Expand() => Owner.IsDropDownOpen = true; |
|||
bool IValueProvider.IsReadOnly => true; |
|||
|
|||
string? IValueProvider.Value |
|||
{ |
|||
get |
|||
{ |
|||
var selection = GetSelection(); |
|||
return selection.Count == 1 ? selection[0].GetName() : null; |
|||
} |
|||
} |
|||
|
|||
void IValueProvider.SetValue(string? value) => throw new NotSupportedException(); |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.ComboBox; |
|||
} |
|||
|
|||
protected override IReadOnlyList<AutomationPeer>? GetSelectionCore() |
|||
{ |
|||
if (ExpandCollapseState == ExpandCollapseState.Expanded) |
|||
return base.GetSelectionCore(); |
|||
|
|||
// If the combo box is not open then we won't have an ItemsPresenter so the default
|
|||
// GetSelectionCore implementation won't work. For this case we create a separate
|
|||
// peer to represent the unrealized item.
|
|||
if (Owner.SelectedItem is object selection) |
|||
{ |
|||
_selection ??= new[] { new UnrealizedSelectionPeer(this) }; |
|||
_selection[0].Item = selection; |
|||
return _selection; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
protected override void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
base.OwnerPropertyChanged(sender, e); |
|||
|
|||
if (e.Property == ComboBox.IsDropDownOpenProperty) |
|||
{ |
|||
RaisePropertyChangedEvent( |
|||
ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, |
|||
ToState((bool)e.OldValue!), |
|||
ToState((bool)e.NewValue!)); |
|||
} |
|||
} |
|||
|
|||
private ExpandCollapseState ToState(bool value) |
|||
{ |
|||
return value ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; |
|||
} |
|||
|
|||
private class UnrealizedSelectionPeer : UnrealizedElementAutomationPeer |
|||
{ |
|||
private readonly ComboBoxAutomationPeer _owner; |
|||
private object? _item; |
|||
|
|||
public UnrealizedSelectionPeer(ComboBoxAutomationPeer owner) |
|||
{ |
|||
_owner = owner; |
|||
} |
|||
|
|||
public object? Item |
|||
{ |
|||
get => _item; |
|||
set |
|||
{ |
|||
if (_item != value) |
|||
{ |
|||
var oldValue = GetNameCore(); |
|||
_item = value; |
|||
RaisePropertyChangedEvent( |
|||
AutomationElementIdentifiers.NameProperty, |
|||
oldValue, |
|||
GetNameCore()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override string? GetAcceleratorKeyCore() => null; |
|||
protected override string? GetAccessKeyCore() => null; |
|||
protected override string? GetAutomationIdCore() => null; |
|||
protected override string GetClassNameCore() => typeof(ComboBoxItem).Name; |
|||
protected override AutomationPeer? GetLabeledByCore() => null; |
|||
protected override AutomationPeer? GetParentCore() => _owner; |
|||
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.ListItem; |
|||
|
|||
protected override string? GetNameCore() |
|||
{ |
|||
if (_item is Control c) |
|||
{ |
|||
var result = AutomationProperties.GetName(c); |
|||
|
|||
if (result is null && c is ContentControl cc && cc.Presenter?.Child is TextBlock text) |
|||
{ |
|||
result = text.Text; |
|||
} |
|||
|
|||
if (result is null) |
|||
{ |
|||
result = c.GetValue(ContentControl.ContentProperty)?.ToString(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return _item?.ToString(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ContentControlAutomationPeer : ControlAutomationPeer |
|||
{ |
|||
protected ContentControlAutomationPeer(ContentControl owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new ContentControl Owner => (ContentControl)base.Owner; |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Pane; |
|||
|
|||
protected override string? GetNameCore() |
|||
{ |
|||
var result = base.GetNameCore(); |
|||
|
|||
if (result is null && Owner.Presenter?.Child is TextBlock text) |
|||
{ |
|||
result = text.Text; |
|||
} |
|||
|
|||
if (result is null) |
|||
{ |
|||
result = Owner.Content?.ToString(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => false; |
|||
protected override bool IsControlElementCore() => false; |
|||
} |
|||
} |
|||
@ -0,0 +1,221 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls; |
|||
using Avalonia.VisualTree; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
/// <summary>
|
|||
/// An automation peer which represents a <see cref="Control"/> element.
|
|||
/// </summary>
|
|||
public class ControlAutomationPeer : AutomationPeer |
|||
{ |
|||
private IReadOnlyList<AutomationPeer>? _children; |
|||
private bool _childrenValid; |
|||
private AutomationPeer? _parent; |
|||
private bool _parentValid; |
|||
|
|||
public ControlAutomationPeer(Control owner) |
|||
{ |
|||
Owner = owner ?? throw new ArgumentNullException("owner"); |
|||
Initialize(); |
|||
} |
|||
|
|||
public Control Owner { get; } |
|||
|
|||
public AutomationPeer GetOrCreate(Control element) |
|||
{ |
|||
if (element == Owner) |
|||
return this; |
|||
return CreatePeerForElement(element); |
|||
} |
|||
|
|||
public static AutomationPeer CreatePeerForElement(Control element) |
|||
{ |
|||
return element.GetOrCreateAutomationPeer(); |
|||
} |
|||
|
|||
protected override void BringIntoViewCore() => Owner.BringIntoView(); |
|||
|
|||
protected override IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore() |
|||
{ |
|||
var children = _children ?? Array.Empty<AutomationPeer>(); |
|||
|
|||
if (_childrenValid) |
|||
return children; |
|||
|
|||
var newChildren = GetChildrenCore() ?? Array.Empty<AutomationPeer>(); |
|||
|
|||
foreach (var peer in children.Except(newChildren)) |
|||
peer.TrySetParent(null); |
|||
foreach (var peer in newChildren) |
|||
peer.TrySetParent(this); |
|||
|
|||
_childrenValid = true; |
|||
return _children = newChildren; |
|||
} |
|||
|
|||
protected virtual IReadOnlyList<AutomationPeer>? GetChildrenCore() |
|||
{ |
|||
var children = ((IVisual)Owner).VisualChildren; |
|||
|
|||
if (children.Count == 0) |
|||
return null; |
|||
|
|||
var result = new List<AutomationPeer>(); |
|||
|
|||
foreach (var child in children) |
|||
{ |
|||
if (child is Control c && c.IsVisible) |
|||
{ |
|||
result.Add(GetOrCreate(c)); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected override AutomationPeer? GetLabeledByCore() |
|||
{ |
|||
var label = AutomationProperties.GetLabeledBy(Owner); |
|||
return label is Control c ? GetOrCreate(c) : null; |
|||
} |
|||
|
|||
protected override string? GetNameCore() |
|||
{ |
|||
var result = AutomationProperties.GetName(Owner); |
|||
|
|||
if (string.IsNullOrWhiteSpace(result) && GetLabeledBy() is AutomationPeer labeledBy) |
|||
{ |
|||
return labeledBy.GetName(); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
protected override AutomationPeer? GetParentCore() |
|||
{ |
|||
EnsureConnected(); |
|||
return _parent; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Invalidates the peer's children and causes a re-read from <see cref="GetChildrenCore"/>.
|
|||
/// </summary>
|
|||
protected void InvalidateChildren() |
|||
{ |
|||
_childrenValid = false; |
|||
RaiseChildrenChangedEvent(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Invalidates the peer's parent.
|
|||
/// </summary>
|
|||
protected void InvalidateParent() |
|||
{ |
|||
_parent = null; |
|||
_parentValid = false; |
|||
} |
|||
|
|||
protected override bool ShowContextMenuCore() |
|||
{ |
|||
var c = Owner; |
|||
|
|||
while (c is object) |
|||
{ |
|||
if (c.ContextMenu is object) |
|||
{ |
|||
c.ContextMenu.Open(c); |
|||
return true; |
|||
} |
|||
|
|||
c = c.Parent as Control; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
protected internal override bool TrySetParent(AutomationPeer? parent) |
|||
{ |
|||
_parent = parent; |
|||
return true; |
|||
} |
|||
|
|||
protected override string? GetAcceleratorKeyCore() => AutomationProperties.GetAcceleratorKey(Owner); |
|||
protected override string? GetAccessKeyCore() => AutomationProperties.GetAccessKey(Owner); |
|||
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom; |
|||
protected override string? GetAutomationIdCore() => AutomationProperties.GetAutomationId(Owner) ?? Owner.Name; |
|||
protected override Rect GetBoundingRectangleCore() => GetBounds(Owner.TransformedBounds); |
|||
protected override string GetClassNameCore() => Owner.GetType().Name; |
|||
protected override bool HasKeyboardFocusCore() => Owner.IsFocused; |
|||
protected override bool IsContentElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Content; |
|||
protected override bool IsControlElementCore() => AutomationProperties.GetAccessibilityView(Owner) >= AccessibilityView.Control; |
|||
protected override bool IsEnabledCore() => Owner.IsEnabled; |
|||
protected override bool IsKeyboardFocusableCore() => Owner.Focusable; |
|||
protected override void SetFocusCore() => Owner.Focus(); |
|||
|
|||
protected override AutomationControlType GetControlTypeOverrideCore() |
|||
{ |
|||
return AutomationProperties.GetControlTypeOverride(Owner) ?? GetAutomationControlTypeCore(); |
|||
} |
|||
|
|||
private static Rect GetBounds(TransformedBounds? bounds) |
|||
{ |
|||
return bounds?.Bounds.TransformToAABB(bounds!.Value.Transform) ?? default; |
|||
} |
|||
|
|||
private void Initialize() |
|||
{ |
|||
Owner.PropertyChanged += OwnerPropertyChanged; |
|||
var visualChildren = ((IVisual)Owner).VisualChildren; |
|||
visualChildren.CollectionChanged += VisualChildrenChanged; |
|||
} |
|||
|
|||
private void VisualChildrenChanged(object? sender, EventArgs e) => InvalidateChildren(); |
|||
|
|||
private void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property == Visual.IsVisibleProperty) |
|||
{ |
|||
var parent = Owner.GetVisualParent(); |
|||
if (parent is Control c) |
|||
(GetOrCreate(c) as ControlAutomationPeer)?.InvalidateChildren(); |
|||
} |
|||
else if (e.Property == Visual.TransformedBoundsProperty) |
|||
{ |
|||
RaisePropertyChangedEvent( |
|||
AutomationElementIdentifiers.BoundingRectangleProperty, |
|||
GetBounds((TransformedBounds?)e.OldValue), |
|||
GetBounds((TransformedBounds?)e.NewValue)); |
|||
} |
|||
else if (e.Property == Visual.VisualParentProperty) |
|||
{ |
|||
InvalidateParent(); |
|||
} |
|||
} |
|||
|
|||
|
|||
private void EnsureConnected() |
|||
{ |
|||
if (!_parentValid) |
|||
{ |
|||
var parent = Owner.GetVisualParent(); |
|||
|
|||
while (parent is object) |
|||
{ |
|||
if (parent is Control c) |
|||
{ |
|||
var parentPeer = GetOrCreate(c); |
|||
parentPeer.GetChildren(); |
|||
} |
|||
|
|||
parent = parent.GetVisualParent(); |
|||
} |
|||
|
|||
_parentValid = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,54 @@ |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ItemsControlAutomationPeer : ControlAutomationPeer, IScrollProvider |
|||
{ |
|||
private bool _searchedForScrollable; |
|||
private IScrollProvider? _scroller; |
|||
|
|||
public ItemsControlAutomationPeer(ItemsControl owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new ItemsControl Owner => (ItemsControl)base.Owner; |
|||
public bool HorizontallyScrollable => _scroller?.HorizontallyScrollable ?? false; |
|||
public double HorizontalScrollPercent => _scroller?.HorizontalScrollPercent ?? -1; |
|||
public double HorizontalViewSize => _scroller?.HorizontalViewSize ?? 0; |
|||
public bool VerticallyScrollable => _scroller?.VerticallyScrollable ?? false; |
|||
public double VerticalScrollPercent => _scroller?.VerticalScrollPercent ?? -1; |
|||
public double VerticalViewSize => _scroller?.VerticalViewSize ?? 0; |
|||
|
|||
protected virtual IScrollProvider? Scroller |
|||
{ |
|||
get |
|||
{ |
|||
if (!_searchedForScrollable) |
|||
{ |
|||
if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable) |
|||
_scroller = GetOrCreate(scrollable) as IScrollProvider; |
|||
_searchedForScrollable = true; |
|||
} |
|||
|
|||
return _scroller; |
|||
} |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.List; |
|||
} |
|||
|
|||
public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) |
|||
{ |
|||
_scroller?.Scroll(horizontalAmount, verticalAmount); |
|||
} |
|||
|
|||
public void SetScrollPercent(double horizontalPercent, double verticalPercent) |
|||
{ |
|||
_scroller?.SetScrollPercent(horizontalPercent, verticalPercent); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
using System; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Primitives; |
|||
using Avalonia.Controls.Selection; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ListItemAutomationPeer : ContentControlAutomationPeer, |
|||
ISelectionItemProvider |
|||
{ |
|||
public ListItemAutomationPeer(ContentControl owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public bool IsSelected => Owner.GetValue(ListBoxItem.IsSelectedProperty); |
|||
|
|||
public ISelectionProvider? SelectionContainer |
|||
{ |
|||
get |
|||
{ |
|||
if (Owner.Parent is Control parent) |
|||
{ |
|||
var parentPeer = GetOrCreate(parent); |
|||
return parentPeer as ISelectionProvider; |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public void Select() |
|||
{ |
|||
EnsureEnabled(); |
|||
|
|||
if (Owner.Parent is SelectingItemsControl parent) |
|||
{ |
|||
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner); |
|||
|
|||
if (index != -1) |
|||
parent.SelectedIndex = index; |
|||
} |
|||
} |
|||
|
|||
void ISelectionItemProvider.AddToSelection() |
|||
{ |
|||
EnsureEnabled(); |
|||
|
|||
if (Owner.Parent is ItemsControl parent && |
|||
parent.GetValue(ListBox.SelectionProperty) is ISelectionModel selectionModel) |
|||
{ |
|||
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner); |
|||
|
|||
if (index != -1) |
|||
selectionModel.Select(index); |
|||
} |
|||
} |
|||
|
|||
void ISelectionItemProvider.RemoveFromSelection() |
|||
{ |
|||
EnsureEnabled(); |
|||
|
|||
if (Owner.Parent is ItemsControl parent && |
|||
parent.GetValue(ListBox.SelectionProperty) is ISelectionModel selectionModel) |
|||
{ |
|||
var index = parent.ItemContainerGenerator.IndexFromContainer(Owner); |
|||
|
|||
if (index != -1) |
|||
selectionModel.Deselect(index); |
|||
} |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.ListItem; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => true; |
|||
protected override bool IsControlElementCore() => true; |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Primitives; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class MenuItemAutomationPeer : ControlAutomationPeer |
|||
{ |
|||
public MenuItemAutomationPeer(MenuItem owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new MenuItem Owner => (MenuItem)base.Owner; |
|||
|
|||
protected override string? GetAccessKeyCore() |
|||
{ |
|||
var result = base.GetAccessKeyCore(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(result)) |
|||
{ |
|||
if (Owner.HeaderPresenter?.Child is AccessText accessText) |
|||
{ |
|||
result = accessText.AccessKey.ToString(); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected override string? GetAcceleratorKeyCore() |
|||
{ |
|||
var result = base.GetAcceleratorKeyCore(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(result)) |
|||
{ |
|||
result = Owner.InputGesture?.ToString(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.MenuItem; |
|||
} |
|||
|
|||
protected override string? GetNameCore() |
|||
{ |
|||
var result = base.GetNameCore(); |
|||
|
|||
if (result is null && Owner.Header is string header) |
|||
{ |
|||
result = AccessText.RemoveAccessKeyMarker(header); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
/// <summary>
|
|||
/// An automation peer which represents an element that is exposed to automation as non-
|
|||
/// interactive or as not contributing to the logical structure of the application.
|
|||
/// </summary>
|
|||
public class NoneAutomationPeer : ControlAutomationPeer |
|||
{ |
|||
public NoneAutomationPeer(Control owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.None; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => false; |
|||
protected override bool IsControlElementCore() => false; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,48 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Diagnostics; |
|||
using Avalonia.Controls.Primitives; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class PopupAutomationPeer : ControlAutomationPeer |
|||
{ |
|||
public PopupAutomationPeer(Popup owner) |
|||
: base(owner) |
|||
{ |
|||
owner.Opened += PopupOpenedClosed; |
|||
owner.Closed += PopupOpenedClosed; |
|||
} |
|||
|
|||
protected override IReadOnlyList<AutomationPeer>? GetChildrenCore() |
|||
{ |
|||
var host = (IPopupHostProvider)Owner; |
|||
return host.PopupHost is Control c ? new[] { GetOrCreate(c) } : null; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => false; |
|||
protected override bool IsControlElementCore() => false; |
|||
|
|||
private void PopupOpenedClosed(object? sender, EventArgs e) |
|||
{ |
|||
// This is golden. We're following WPF's automation peer API here where the
|
|||
// parent of a peer is set when another peer returns it as a child. We want to
|
|||
// add the popup root as a child of the popup, so we need to return it as a
|
|||
// child right? Yeah except invalidating children doesn't automatically cause
|
|||
// UIA to re-read the children meaning that the parent doesn't get set. So the
|
|||
// MAIN MECHANISM FOR PARENTING CONTROLS IS BROKEN WITH THE ONLY AUTOMATION API
|
|||
// IT WAS WRITTEN FOR. Luckily WPF provides an escape-hatch by exposing the
|
|||
// TrySetParent API internally to work around this. We're exposing it publicly
|
|||
// to shame whoever came up with this abomination of an API.
|
|||
GetPopupRoot()?.TrySetParent(this); |
|||
InvalidateChildren(); |
|||
} |
|||
|
|||
private AutomationPeer? GetPopupRoot() |
|||
{ |
|||
var popupRoot = ((IPopupHostProvider)Owner).PopupHost as Control; |
|||
return popupRoot is object ? GetOrCreate(popupRoot) : null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
using Avalonia.Controls.Primitives; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class PopupRootAutomationPeer : WindowBaseAutomationPeer |
|||
{ |
|||
public PopupRootAutomationPeer(PopupRoot owner) |
|||
: base(owner) |
|||
{ |
|||
if (owner.IsVisible) |
|||
StartTrackingFocus(); |
|||
else |
|||
owner.Opened += OnOpened; |
|||
owner.Closed += OnClosed; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => false; |
|||
protected override bool IsControlElementCore() => false; |
|||
|
|||
|
|||
protected override AutomationPeer? GetParentCore() |
|||
{ |
|||
var parent = base.GetParentCore(); |
|||
return parent; |
|||
} |
|||
|
|||
private void OnOpened(object? sender, EventArgs e) |
|||
{ |
|||
((PopupRoot)Owner).Opened -= OnOpened; |
|||
StartTrackingFocus(); |
|||
} |
|||
|
|||
private void OnClosed(object? sender, EventArgs e) |
|||
{ |
|||
((PopupRoot)Owner).Closed -= OnClosed; |
|||
StopTrackingFocus(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls.Primitives; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public abstract class RangeBaseAutomationPeer : ControlAutomationPeer, IRangeValueProvider |
|||
{ |
|||
public RangeBaseAutomationPeer(RangeBase owner) |
|||
: base(owner) |
|||
{ |
|||
owner.PropertyChanged += OwnerPropertyChanged; |
|||
} |
|||
|
|||
public new RangeBase Owner => (RangeBase)base.Owner; |
|||
public virtual bool IsReadOnly => false; |
|||
public double Maximum => Owner.Maximum; |
|||
public double Minimum => Owner.Minimum; |
|||
public double Value => Owner.Value; |
|||
public double SmallChange => Owner.SmallChange; |
|||
public double LargeChange => Owner.LargeChange; |
|||
|
|||
public void SetValue(double value) => Owner.Value = value; |
|||
|
|||
protected virtual void OwnerPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) |
|||
{ |
|||
if (e.Property == RangeBase.MinimumProperty) |
|||
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.MinimumProperty, e.OldValue, e.NewValue); |
|||
else if (e.Property == RangeBase.MaximumProperty) |
|||
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.MaximumProperty, e.OldValue, e.NewValue); |
|||
else if (e.Property == RangeBase.ValueProperty) |
|||
RaisePropertyChangedEvent(RangeValuePatternIdentifiers.ValueProperty, e.OldValue, e.NewValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,172 @@ |
|||
using System; |
|||
using Avalonia.Automation.Provider; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Utilities; |
|||
|
|||
namespace Avalonia.Automation.Peers |
|||
{ |
|||
public class ScrollViewerAutomationPeer : ControlAutomationPeer, IScrollProvider |
|||
{ |
|||
public ScrollViewerAutomationPeer(ScrollViewer owner) |
|||
: base(owner) |
|||
{ |
|||
} |
|||
|
|||
public new ScrollViewer Owner => (ScrollViewer)base.Owner; |
|||
|
|||
public bool HorizontallyScrollable |
|||
{ |
|||
get => MathUtilities.GreaterThan(Owner.Extent.Width, Owner.Viewport.Width); |
|||
} |
|||
|
|||
public double HorizontalScrollPercent |
|||
{ |
|||
get |
|||
{ |
|||
if (!HorizontallyScrollable) |
|||
return ScrollPatternIdentifiers.NoScroll; |
|||
return (double)(Owner.Offset.X * 100.0 / (Owner.Extent.Width - Owner.Viewport.Width)); |
|||
} |
|||
} |
|||
|
|||
public double HorizontalViewSize |
|||
{ |
|||
get |
|||
{ |
|||
if (MathUtilities.IsZero(Owner.Extent.Width)) |
|||
return 100; |
|||
return Math.Min(100, Owner.Viewport.Width * 100.0 / Owner.Extent.Width); |
|||
} |
|||
} |
|||
|
|||
public bool VerticallyScrollable |
|||
{ |
|||
get => MathUtilities.GreaterThan(Owner.Extent.Height, Owner.Viewport.Height); |
|||
} |
|||
|
|||
public double VerticalScrollPercent |
|||
{ |
|||
get |
|||
{ |
|||
if (!VerticallyScrollable) |
|||
return ScrollPatternIdentifiers.NoScroll; |
|||
return (double)(Owner.Offset.Y * 100.0 / (Owner.Extent.Height - Owner.Viewport.Height)); |
|||
} |
|||
} |
|||
|
|||
public double VerticalViewSize |
|||
{ |
|||
get |
|||
{ |
|||
if (MathUtilities.IsZero(Owner.Extent.Height)) |
|||
return 100; |
|||
return Math.Min(100, Owner.Viewport.Height * 100.0 / Owner.Extent.Height); |
|||
} |
|||
} |
|||
|
|||
protected override AutomationControlType GetAutomationControlTypeCore() |
|||
{ |
|||
return AutomationControlType.Pane; |
|||
} |
|||
|
|||
protected override bool IsContentElementCore() => false; |
|||
|
|||
protected override bool IsControlElementCore() |
|||
{ |
|||
// Return false if the control is part of a control template.
|
|||
return Owner.TemplatedParent is null && base.IsControlElementCore(); |
|||
} |
|||
|
|||
public void Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount) |
|||
{ |
|||
if (!IsEnabled()) |
|||
throw new ElementNotEnabledException(); |
|||
|
|||
var scrollHorizontally = horizontalAmount != ScrollAmount.NoAmount; |
|||
var scrollVertically = verticalAmount != ScrollAmount.NoAmount; |
|||
|
|||
if (scrollHorizontally && !HorizontallyScrollable || scrollVertically && !VerticallyScrollable) |
|||
{ |
|||
throw new InvalidOperationException("Operation cannot be performed"); |
|||
} |
|||
|
|||
switch (horizontalAmount) |
|||
{ |
|||
case ScrollAmount.LargeDecrement: |
|||
Owner.PageLeft(); |
|||
break; |
|||
case ScrollAmount.SmallDecrement: |
|||
Owner.LineLeft(); |
|||
break; |
|||
case ScrollAmount.SmallIncrement: |
|||
Owner.LineRight(); |
|||
break; |
|||
case ScrollAmount.LargeIncrement: |
|||
Owner.PageRight(); |
|||
break; |
|||
case ScrollAmount.NoAmount: |
|||
break; |
|||
default: |
|||
throw new InvalidOperationException("Operation cannot be performed"); |
|||
} |
|||
|
|||
switch (verticalAmount) |
|||
{ |
|||
case ScrollAmount.LargeDecrement: |
|||
Owner.PageUp(); |
|||
break; |
|||
case ScrollAmount.SmallDecrement: |
|||
Owner.LineUp(); |
|||
break; |
|||
case ScrollAmount.SmallIncrement: |
|||
Owner.LineDown(); |
|||
break; |
|||
case ScrollAmount.LargeIncrement: |
|||
Owner.PageDown(); |
|||
break; |
|||
case ScrollAmount.NoAmount: |
|||
break; |
|||
default: |
|||
throw new InvalidOperationException("Operation cannot be performed"); |
|||
} |
|||
} |
|||
|
|||
public void SetScrollPercent(double horizontalPercent, double verticalPercent) |
|||
{ |
|||
if (!IsEnabled()) |
|||
throw new ElementNotEnabledException(); |
|||
|
|||
var scrollHorizontally = horizontalPercent != ScrollPatternIdentifiers.NoScroll; |
|||
var scrollVertically = verticalPercent != ScrollPatternIdentifiers.NoScroll; |
|||
|
|||
if (scrollHorizontally && !HorizontallyScrollable || scrollVertically && !VerticallyScrollable) |
|||
{ |
|||
throw new InvalidOperationException("Operation cannot be performed"); |
|||
} |
|||
|
|||
if (scrollHorizontally && (horizontalPercent < 0.0) || (horizontalPercent > 100.0)) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("horizontalPercent"); |
|||
} |
|||
|
|||
if (scrollVertically && (verticalPercent < 0.0) || (verticalPercent > 100.0)) |
|||
{ |
|||
throw new ArgumentOutOfRangeException("verticalPercent"); |
|||
} |
|||
|
|||
var offset = Owner.Offset; |
|||
|
|||
if (scrollHorizontally) |
|||
{ |
|||
offset = offset.WithX((Owner.Extent.Width - Owner.Viewport.Width) * horizontalPercent * 0.01); |
|||
} |
|||
|
|||
if (scrollVertically) |
|||
{ |
|||
offset = offset.WithY((Owner.Extent.Height - Owner.Viewport.Height) * verticalPercent * 0.01); |
|||
} |
|||
|
|||
Owner.Offset = offset; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue