Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/xwt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBret Johnson <bret.johnson@microsoft.com>2018-06-25 19:36:35 +0300
committerBret Johnson <bret.johnson@microsoft.com>2018-06-25 20:27:10 +0300
commit180d2d95ab726946768cb0251da54c85585c5f59 (patch)
treed55ea853e19c92517949cd7ed2e6d37ca55d1b15
parent533c6ee659cf0174436937ffb4617b6ba562abd6 (diff)
[a11y][WPF] Added accessibilty support for popover windows
Previously, we weren't setting the automation Name on the window for popovers, which this PR addresses, fixing: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/594024 https://devdiv.visualstudio.com/DevDiv/_workitems/edit/595199 Popovers are special for a couple of reasons: - On the Xwt side, they derive from XwtComponent instead of XwtWidget. - On the WPF side, accessibility info for the window should be set on the PopupRoot, which doesn't exist until the popup is actually shown. Thus we need to delay setting the accessibility settings until the popup is shown. These changes pass along accessbility settings for the non-widget popovers, hold onto them, and delay setting them until the Show.
-rw-r--r--Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs5
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/AccessibleBackend.cs50
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/DropDownButton.cs1
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/PopoverBackend.cs31
-rw-r--r--Xwt.WPF/Xwt.WPFBackend/WindowsSpinButton.xaml.cs20
-rw-r--r--Xwt.XamMac/Xwt.Mac/AccessibleBackend.cs5
-rw-r--r--Xwt/Xwt.Accessibility/Accessible.cs24
-rw-r--r--Xwt/Xwt.Accessibility/IAccessibleBackend.cs4
-rw-r--r--Xwt/Xwt/Popover.cs13
9 files changed, 131 insertions, 22 deletions
diff --git a/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs b/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
index 2bd393cb..c9726150 100644
--- a/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
+++ b/Xwt.Gtk/Xwt.GtkBackend/AccessibleBackend.cs
@@ -62,6 +62,11 @@ namespace Xwt.GtkBackend
Initialize (backend?.Widget, eventSink);
}
+ public void Initialize (IPopoverBackend parentPopover, IAccessibleEventSink eventSink)
+ {
+ // Not currently supported
+ }
+
public void Initialize (object parentWidget, IAccessibleEventSink eventSink)
{
this.eventSink = eventSink;
diff --git a/Xwt.WPF/Xwt.WPFBackend/AccessibleBackend.cs b/Xwt.WPF/Xwt.WPFBackend/AccessibleBackend.cs
index 803a0511..49d5b2d9 100644
--- a/Xwt.WPF/Xwt.WPFBackend/AccessibleBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/AccessibleBackend.cs
@@ -5,6 +5,8 @@ using System.Text;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
+using System.Windows.Controls.Primitives;
+using System.Windows.Media;
using Xwt.Accessibility;
using Xwt.Backends;
@@ -18,24 +20,59 @@ namespace Xwt.WPFBackend
public bool IsAccessible { get; set; }
+ private string identifier;
public string Identifier {
get { return AutomationProperties.GetAutomationId (element); }
- set { AutomationProperties.SetAutomationId (element, value); }
+ set {
+ identifier = value;
+ AutomationProperties.SetAutomationId (element, value);
+ }
}
+
+ private string label;
public string Label {
get { return AutomationProperties.GetName (element); }
- set { AutomationProperties.SetName (element, value); }
+ set {
+ label = value;
+ AutomationProperties.SetName (element, value);
+ }
}
+
+ private string description;
public string Description {
get { return AutomationProperties.GetHelpText (element); }
- set { AutomationProperties.SetHelpText (element, value); }
+ set {
+ description = value;
+ AutomationProperties.SetHelpText (element, value);
+ }
}
+
+ private Widget labelWidget;
public Widget LabelWidget {
set {
+ labelWidget = value;
AutomationProperties.SetLabeledBy (element, (Toolkit.GetBackend (value) as WidgetBackend)?.Widget);
}
}
+ /// <summary>
+ /// In some cases (just Popovers currently) we need to wait to set the automation properties until the element
+ /// that needs them comes into existence (a PopoverRoot in the case of a Popover, when it's shown). This is
+ /// used for that delayed initialization.
+ /// </summary>
+ /// <param name="element">UIElement on which to set the properties</param>
+ public void InitAutomationProperties (UIElement element)
+ {
+ if (identifier != null)
+ AutomationProperties.SetAutomationId (element, identifier);
+ if (label != null)
+ AutomationProperties.SetName (element, label);
+ if (description != null)
+ AutomationProperties.SetHelpText (element, description);
+ if (labelWidget != null)
+ AutomationProperties.SetLabeledBy (element, (Toolkit.GetBackend (labelWidget) as WidgetBackend)?.Widget);
+ }
+
public string Title { get; set; }
public string Value { get; set; }
public Role Role { get; set; } = Role.Custom;
@@ -59,6 +96,13 @@ namespace Xwt.WPFBackend
wpfBackend.HasAccessibleObject = true;
}
+ public void Initialize (IPopoverBackend parentPopover, IAccessibleEventSink eventSink)
+ {
+ var popoverBackend = (PopoverBackend) parentPopover;
+ Popup popup = popoverBackend.NativeWidget;
+ Initialize (popup, eventSink);
+ }
+
public void Initialize (object parentWidget, IAccessibleEventSink eventSink)
{
this.element = parentWidget as UIElement;
diff --git a/Xwt.WPF/Xwt.WPFBackend/DropDownButton.cs b/Xwt.WPF/Xwt.WPFBackend/DropDownButton.cs
index 5b7934d1..704cd442 100644
--- a/Xwt.WPF/Xwt.WPFBackend/DropDownButton.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/DropDownButton.cs
@@ -159,7 +159,6 @@ namespace Xwt.WPFBackend
owner.IsChecked = true;
}
-
public void Collapse ()
{
owner.IsChecked = false;
diff --git a/Xwt.WPF/Xwt.WPFBackend/PopoverBackend.cs b/Xwt.WPF/Xwt.WPFBackend/PopoverBackend.cs
index 3203682c..afedab29 100644
--- a/Xwt.WPF/Xwt.WPFBackend/PopoverBackend.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/PopoverBackend.cs
@@ -1,4 +1,4 @@
-//
+//
// PopoverBackend.cs
//
// Author:
@@ -58,10 +58,28 @@ namespace Xwt.WPFBackend
get { return (Popover)base.frontend; }
}
- System.Windows.Controls.Primitives.Popup NativeWidget {
+ public System.Windows.Controls.Primitives.Popup NativeWidget {
get; set;
}
+ /// <summary>
+ /// Search up the visual tree, finding the PopupRoot for the popup.
+ /// </summary>
+ /// <returns>PopupRoot or null if not found for some reason</returns>
+ public FrameworkElement GetPopupRoot ()
+ {
+ FrameworkElement element = Border;
+
+ do {
+ element = (FrameworkElement) VisualTreeHelper.GetParent (element);
+ if (element == null)
+ return null;
+
+ if (element.GetType ().Name == "PopupRoot")
+ return element;
+ } while (true);
+ }
+
public PopoverBackend ()
{
Border = new System.Windows.Controls.Border {
@@ -113,6 +131,15 @@ namespace Xwt.WPFBackend
};
NativeWidget.PlacementTarget = (System.Windows.FrameworkElement)Context.Toolkit.GetNativeWidget (reference);
NativeWidget.IsOpen = true;
+
+ // Popups are special in that the automation properties need to be set on the PopupRoot, which only exists when the popup is shown
+ // See https://social.msdn.microsoft.com/Forums/vstudio/en-US/d4ba12c8-7a87-478e-b064-5620f929a0cf/how-to-set-automationid-and-name-for-popup?forum=wpf
+ var accessibleBackend = (AccessibleBackend)Toolkit.GetBackend (Frontend.Accessible);
+ if (accessibleBackend != null) {
+ FrameworkElement popupRoot = GetPopupRoot ();
+ if (popupRoot != null)
+ accessibleBackend.InitAutomationProperties (popupRoot);
+ }
}
void NativeWidget_Closed (object sender, EventArgs e)
diff --git a/Xwt.WPF/Xwt.WPFBackend/WindowsSpinButton.xaml.cs b/Xwt.WPF/Xwt.WPFBackend/WindowsSpinButton.xaml.cs
index 361b91b4..cb212fa4 100644
--- a/Xwt.WPF/Xwt.WPFBackend/WindowsSpinButton.xaml.cs
+++ b/Xwt.WPF/Xwt.WPFBackend/WindowsSpinButton.xaml.cs
@@ -168,8 +168,8 @@ namespace Xwt.WPFBackend
mainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
mainGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(16) });
- //Textbox
- textBox = new SpinButtonTextBox (this);
+ //Textbox
+ textBox = new SpinButtonTextBox (this);
textBox.Text = "0";
textBox.HorizontalAlignment = HorizontalAlignment.Stretch;
textBox.MinWidth = 25;
@@ -249,7 +249,7 @@ namespace Xwt.WPFBackend
public TextBox TextBox => textBox;
- private void UserControl_Loaded(object sender, RoutedEventArgs e)
+ private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
UpdateTextbox();
}
@@ -459,19 +459,19 @@ namespace Xwt.WPFBackend
#endregion
#region Accessibility
- class SpinButtonTextBox : TextBox
- {
- WindowsSpinButton spinButton;
+ class SpinButtonTextBox : TextBox
+ {
+ WindowsSpinButton spinButton;
public SpinButtonTextBox (WindowsSpinButton spinButton)
{
this.spinButton = spinButton;
}
- protected override AutomationPeer OnCreateAutomationPeer ()
- {
- return new WindowsSpinButtonAutomationPeer (spinButton);
- }
+ protected override AutomationPeer OnCreateAutomationPeer ()
+ {
+ return new WindowsSpinButtonAutomationPeer (spinButton);
+ }
}
class WindowsSpinButtonAutomationPeer : UserControlAutomationPeer, IRangeValueProvider
diff --git a/Xwt.XamMac/Xwt.Mac/AccessibleBackend.cs b/Xwt.XamMac/Xwt.Mac/AccessibleBackend.cs
index e1aa0e14..24ccc6b6 100644
--- a/Xwt.XamMac/Xwt.Mac/AccessibleBackend.cs
+++ b/Xwt.XamMac/Xwt.Mac/AccessibleBackend.cs
@@ -49,6 +49,11 @@ namespace Xwt.Mac
Initialize (parentBackend?.Widget, eventSink);
}
+ public void Initialize (IPopoverBackend parentPopover, IAccessibleEventSink eventSink)
+ {
+ // Not currently supported
+ }
+
public void Initialize (object parentWidget, IAccessibleEventSink eventSink)
{
this.eventSink = eventSink;
diff --git a/Xwt/Xwt.Accessibility/Accessible.cs b/Xwt/Xwt.Accessibility/Accessible.cs
index 836754d3..3075737a 100644
--- a/Xwt/Xwt.Accessibility/Accessible.cs
+++ b/Xwt/Xwt.Accessibility/Accessible.cs
@@ -1,4 +1,4 @@
-//
+//
// Accessible.cs
//
// Author:
@@ -50,9 +50,12 @@ namespace Xwt.Accessibility
protected override void OnBackendCreated ()
{
- var parentBackend = Parent.parentComponent?.GetBackend () as IWidgetBackend;
- if (parentBackend != null)
- Backend.Initialize (parentBackend, this);
+ object parentBackend = Parent.parentComponent?.GetBackend ();
+
+ if (parentBackend is IWidgetBackend)
+ Backend.Initialize ((IWidgetBackend) parentBackend, this);
+ else if (parentBackend is IPopoverBackend)
+ Backend.Initialize ((IPopoverBackend) parentBackend, this);
else
Backend.Initialize (Parent.parentNativeObject, this);
}
@@ -72,6 +75,15 @@ namespace Xwt.Accessibility
backendHost.Parent = this;
}
+ internal Accessible (Popover parent)
+ {
+ if (parent == null)
+ throw new ArgumentNullException (nameof (parent));
+ parentComponent = parent;
+ backendHost = new AccessibleBackendHost ();
+ backendHost.Parent = this;
+ }
+
internal Accessible (object nativeParent)
{
if (nativeParent == null)
@@ -276,6 +288,10 @@ namespace Xwt.Accessibility
{
}
+ public void Initialize (IPopoverBackend parentPopover, IAccessibleEventSink eventSink)
+ {
+ }
+
public void Initialize (object parentWidget, IAccessibleEventSink eventSink)
{
}
diff --git a/Xwt/Xwt.Accessibility/IAccessibleBackend.cs b/Xwt/Xwt.Accessibility/IAccessibleBackend.cs
index 9ee3d9e1..a0f8be9d 100644
--- a/Xwt/Xwt.Accessibility/IAccessibleBackend.cs
+++ b/Xwt/Xwt.Accessibility/IAccessibleBackend.cs
@@ -1,4 +1,4 @@
-//
+//
// AccessibleBackendHandler.cs
//
// Author:
@@ -32,6 +32,8 @@ namespace Xwt.Backends
{
void Initialize (IWidgetBackend parentWidget, IAccessibleEventSink eventSink);
+ void Initialize (IPopoverBackend parentPopover, IAccessibleEventSink eventSink);
+
void Initialize (object parentWidget, IAccessibleEventSink eventSink);
bool IsAccessible { get; set; }
diff --git a/Xwt/Xwt/Popover.cs b/Xwt/Xwt/Popover.cs
index 1f2ada42..6edbdb8b 100644
--- a/Xwt/Xwt/Popover.cs
+++ b/Xwt/Xwt/Popover.cs
@@ -25,6 +25,7 @@
// THE SOFTWARE.
using System;
+using Xwt.Accessibility;
using Xwt.Drawing;
using Xwt.Backends;
@@ -84,7 +85,17 @@ namespace Xwt
VerifyConstructorCall (this);
Content = content;
}
-
+
+ Accessible accessible;
+ public Accessible Accessible {
+ get {
+ if (accessible == null) {
+ accessible = new Accessible (this);
+ }
+ return accessible;
+ }
+ }
+
public Widget Content {
get { return content; }
set {