Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Radial Gauge accessibility: high contrast and narrator #2522

Merged
merged 23 commits into from Oct 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
06139f4
Merge pull request #3 from windows-toolkit/master
XamlBrewer Oct 2, 2018
d08f05c
Radial Gauge accessibility: high contrast and narrator
XamlBrewer Oct 2, 2018
b2b85d6
Removed obsolete theme dictionaries
XamlBrewer Oct 2, 2018
e60ce0b
Added file header
XamlBrewer Oct 3, 2018
cb28c09
Merge branch 'master' into dev
nmetulev Oct 3, 2018
2b0289a
Never override High Contrast theme.
XamlBrewer Oct 8, 2018
342e07a
Update file header
XamlBrewer Oct 8, 2018
6ed817e
Reordered using directives
XamlBrewer Oct 8, 2018
b779461
Private field names and location
XamlBrewer Oct 8, 2018
d106ac4
Restore System-Light-Dark theme after return from High Contrast.
XamlBrewer Oct 8, 2018
f835444
Merge branch 'master' into dev
nmetulev Oct 8, 2018
895674a
Reordered members and removed obsolete code
XamlBrewer Oct 9, 2018
a2adedf
Merge branch 'dev' of https://github.com/XamlBrewer/UWPCommunityToolk…
XamlBrewer Oct 9, 2018
f21c8a9
Merge branch 'master' into dev
nmetulev Oct 10, 2018
f27cfcd
RadialGauge: expose RangeValue properties to Narrator
XamlBrewer Oct 10, 2018
c839c53
Merge branch 'dev' of https://github.com/XamlBrewer/UWPCommunityToolk…
XamlBrewer Oct 10, 2018
ecd2ebd
RadialGauge HighContrast: use Theme Dictionary and avoid hardcoded co…
XamlBrewer Oct 12, 2018
e7c06d1
Merge branch 'master' into dev
nmetulev Oct 15, 2018
e4b6649
Merge branch 'master' into dev
nmetulev Oct 16, 2018
87ffbb1
Radial Gauge: improved event wiring
XamlBrewer Oct 17, 2018
8abd952
Radial Gauge: unregistered Unloaded
XamlBrewer Oct 18, 2018
20ed0ae
Merge branch 'master' into dev
nmetulev Oct 19, 2018
5029010
Merge branch 'master' into dev
nmetulev Oct 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 88 additions & 4 deletions Microsoft.Toolkit.Uwp.UI.Controls/RadialGauge/RadialGauge.cs
Expand Up @@ -4,13 +4,13 @@

using System;
using System.Numerics;
using Windows.ApplicationModel;
using Microsoft.Toolkit.Uwp.UI.Helpers;
using Windows.Foundation;
using Windows.System;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
Expand Down Expand Up @@ -176,6 +176,15 @@ public class RadialGauge : Control
// For convenience.
private const double Degrees2Radians = Math.PI / 180;

// High-contrast accessibility
private static readonly ThemeListener ThemeListener = new ThemeListener();
private SolidColorBrush _needleBrush;
private Brush _trailBrush;
private Brush _scaleBrush;
private SolidColorBrush _scaleTickBrush;
private SolidColorBrush _tickBrush;
private Brush _foreground;

private double _normalizedMinAngle;
private double _normalizedMaxAngle;

Expand All @@ -191,7 +200,12 @@ public RadialGauge()
{
DefaultStyleKey = typeof(RadialGauge);

KeyDown += RadialGauge_KeyDown;
Unloaded += RadialGauge_Unloaded;
}

private void ThemeListener_ThemeChanged(ThemeListener sender)
{
OnColorsChanged();
}

private void RadialGauge_KeyDown(object sender, KeyRoutedEventArgs e)
Expand All @@ -217,6 +231,15 @@ private void RadialGauge_KeyDown(object sender, KeyRoutedEventArgs e)
}
}

private void RadialGauge_Unloaded(object sender, RoutedEventArgs e)
{
// Unregister event handlers.
XamlBrewer marked this conversation as resolved.
Show resolved Hide resolved
KeyDown -= RadialGauge_KeyDown;
ThemeListener.ThemeChanged -= ThemeListener_ThemeChanged;
PointerReleased -= RadialGauge_PointerReleased;
Unloaded -= RadialGauge_Unloaded;
}

/// <summary>
/// Gets or sets the minimum value of the scale.
/// </summary>
Expand Down Expand Up @@ -438,13 +461,32 @@ protected double ValueAngle
/// <value>The maximum angle, in the range from -180 to 540.</value>
protected double NormalizedMaxAngle => _normalizedMaxAngle;

/// <inheritdoc/>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new RadialGaugeAutomationPeer(this);
}

/// <summary>
/// Update the visual state of the control when its template is changed.
/// </summary>
protected override void OnApplyTemplate()
{
// Remember local brushes.
_needleBrush = ReadLocalValue(NeedleBrushProperty) as SolidColorBrush;
_trailBrush = ReadLocalValue(TrailBrushProperty) as SolidColorBrush;
_scaleBrush = ReadLocalValue(ScaleBrushProperty) as SolidColorBrush;
_scaleTickBrush = ReadLocalValue(ScaleTickBrushProperty) as SolidColorBrush;
_tickBrush = ReadLocalValue(TickBrushProperty) as SolidColorBrush;
_foreground = ReadLocalValue(ForegroundProperty) as SolidColorBrush;

// Register event handlers.
PointerReleased += RadialGauge_PointerReleased;
OnScaleChanged(this);
ThemeListener.ThemeChanged += ThemeListener_ThemeChanged;
KeyDown += RadialGauge_KeyDown;

// Apply color scheme.
OnColorsChanged();

base.OnApplyTemplate();
}
Expand Down Expand Up @@ -650,6 +692,48 @@ private static void OnFaceChanged(DependencyObject d)
OnValueChanged(radialGauge);
}

private void OnColorsChanged()
XamlBrewer marked this conversation as resolved.
Show resolved Hide resolved
{
if (ThemeListener.IsHighContrast)
{
// Apply High Contrast Theme.
ClearBrush(_needleBrush, NeedleBrushProperty);
ClearBrush(_trailBrush, TrailBrushProperty);
ClearBrush(_scaleBrush, ScaleBrushProperty);
ClearBrush(_scaleBrush, ScaleTickBrushProperty);
ClearBrush(_tickBrush, TickBrushProperty);
ClearBrush(_foreground, ForegroundProperty);
}
else
{
// Apply User Defined or Default Theme.
RestoreBrush(_needleBrush, NeedleBrushProperty);
RestoreBrush(_trailBrush, TrailBrushProperty);
RestoreBrush(_scaleBrush, ScaleBrushProperty);
RestoreBrush(_scaleBrush, ScaleTickBrushProperty);
RestoreBrush(_tickBrush, TickBrushProperty);
RestoreBrush(_foreground, ForegroundProperty);
}

OnScaleChanged(this);
}

private void ClearBrush(Brush brush, DependencyProperty prop)
{
if (brush != null)
{
ClearValue(prop);
}
}

private void RestoreBrush(Brush source, DependencyProperty prop)
{
if (source != null)
{
SetValue(prop, source);
}
}

private void RadialGauge_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
SetGaugeValueFromPoint(e.Position);
Expand Down
85 changes: 78 additions & 7 deletions Microsoft.Toolkit.Uwp.UI.Controls/RadialGauge/RadialGauge.xaml
Expand Up @@ -2,14 +2,85 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls">

<ResourceDictionary.ThemeDictionaries>

<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="RadialGaugeNeedleBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeTrailBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeTickBrush"
Color="{ThemeResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="RadialGaugeForeground"
Color="{ThemeResource SystemBaseHighColor}" />
</ResourceDictionary>

<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="RadialGaugeNeedleBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeTrailBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeTickBrush"
Color="{ThemeResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="RadialGaugeForeground"
Color="{ThemeResource SystemBaseHighColor}" />
</ResourceDictionary>

<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="RadialGaugeNeedleBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeTrailBrush"
Color="{ThemeResource SystemChromeHighColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
Color="{ThemeResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="RadialGaugeTickBrush"
Color="{ThemeResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="RadialGaugeForeground"
Color="{ThemeResource SystemBaseHighColor}" />
</ResourceDictionary>

<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="RadialGaugeNeedleBrush"
Color="{ThemeResource SystemColorHotlightColor}" />
<SolidColorBrush x:Key="RadialGaugeTrailBrush"
Color="{ThemeResource SystemColorHotlightColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleBrush"
Color="{ThemeResource SystemColorWindowColor}" />
<SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
Color="{ThemeResource SystemColorWindowColor}" />
<SolidColorBrush x:Key="RadialGaugeTickBrush"
Color="{ThemeResource SystemColorWindowTextColor}" />
<SolidColorBrush x:Key="RadialGaugeForeground"
Color="{ThemeResource SystemColorWindowTextColor}" />
</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>

<Style TargetType="local:RadialGauge">
<Setter Property="NeedleBrush" Value="{ThemeResource SystemControlBackgroundAccentBrush}" />
<Setter Property="TrailBrush" Value="{ThemeResource SystemControlBackgroundAccentBrush}" />
<Setter Property="ScaleBrush" Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
<Setter Property="ScaleTickBrush" Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
<Setter Property="TickBrush" Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="NeedleBrush"
Value="{ThemeResource RadialGaugeNeedleBrush}" />
<Setter Property="TrailBrush"
Value="{ThemeResource RadialGaugeTrailBrush}" />
<Setter Property="ScaleBrush"
Value="{ThemeResource RadialGaugeScaleBrush}" />
<Setter Property="ScaleTickBrush"
Value="{ThemeResource RadialGaugeScaleTickBrush}" />
<Setter Property="TickBrush"
Value="{ThemeResource RadialGaugeTickBrush}" />
<Setter Property="Foreground"
Value="{ThemeResource RadialGaugeForeground}" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:RadialGauge">
Expand Down
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Windows.UI.Xaml.Automation.Peers;
using Windows.UI.Xaml.Automation.Provider;

namespace Microsoft.Toolkit.Uwp.UI.Controls
{
/// <summary>
/// Exposes <see cref="RadialGauge"/> to Microsoft UI Automation.
/// </summary>
public class RadialGaugeAutomationPeer :
FrameworkElementAutomationPeer,
IRangeValueProvider
{
/// <summary>
/// Initializes a new instance of the <see cref="RadialGaugeAutomationPeer"/> class.
/// </summary>
/// <param name="owner">The owner element to create for.</param>
public RadialGaugeAutomationPeer(RadialGauge owner)
: base(owner)
{
}

/// <inheritdoc/>
public bool IsReadOnly => !((RadialGauge)Owner).IsInteractive;

/// <inheritdoc/>
public double LargeChange => ((RadialGauge)Owner).StepSize;

/// <inheritdoc/>
public double Maximum => ((RadialGauge)Owner).Maximum;

/// <inheritdoc/>
public double Minimum => ((RadialGauge)Owner).Minimum;

/// <inheritdoc/>
public double SmallChange => ((RadialGauge)Owner).StepSize;

/// <inheritdoc/>
public double Value => ((RadialGauge)Owner).Value;

/// <inheritdoc/>
public void SetValue(double value)
{
((RadialGauge)Owner).Value = value;
}

/// <inheritdoc/>
protected override IList<AutomationPeer> GetChildrenCore()
{
return null;
}

/// <inheritdoc/>
protected override string GetNameCore()
{
var gauge = (RadialGauge)Owner;
return "radial gauge. " + (string.IsNullOrWhiteSpace(gauge.Unit) ? "no unit specified. " : "unit " + gauge.Unit + ". ");
}

/// <inheritdoc/>
protected override object GetPatternCore(PatternInterface patternInterface)
{
if (patternInterface == PatternInterface.RangeValue)
{
// Expose RangeValue properties.
return this;
}

return base.GetPatternCore(patternInterface);
}

/// <inheritdoc/>
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
}
}