Skip to content

Commit 6459df7

Browse files
committed
fix(accessibility): fix some issues with nvda announces
- improve path "browse" button announcement - replace the progress message "label" to readonly textbox, so that it can be focused and anounced by nvda - Adding a lock to avoid a race condition in WPFEventsHandler regarding progress dialog initialisation.
1 parent 1058bfb commit 6459df7

5 files changed

Lines changed: 83 additions & 38 deletions

File tree

Common/DaisyAddinWPFLib/ConversionProgress.xaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616
Minimum="0"
1717
Maximum="100"
1818
Value="0"
19-
IsIndeterminate="False" />
20-
<Label x:Name="CurrentAction"
21-
Margin="5,5,5,0"
22-
Content="" />
19+
IsIndeterminate="False"/>
20+
<TextBox x:Name="CurrentAction"
21+
Margin="5,5,5,0"
22+
Text=""
23+
AutomationProperties.Name="Progress"
24+
AutomationProperties.HelpText=""
25+
IsReadOnly="True"/>
2326
<Expander Header="Details"
2427
ExpandDirection="Down"
2528
IsExpanded="True"

Common/DaisyAddinWPFLib/ConversionProgress.xaml.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System;
33
using System.Threading;
44
using System.Windows;
5+
using System.Windows.Automation;
6+
using System.Windows.Automation.Peers;
57
using System.Windows.Threading;
68
namespace Daisy.SaveAsDAISY.WPF
79
{
@@ -10,11 +12,15 @@ namespace Daisy.SaveAsDAISY.WPF
1012
/// </summary>
1113
public partial class ConversionProgress : Window
1214
{
13-
private string CurrentProgressMessage = "";
15+
public string CurrentProgressMessage { get; set; } = "";
16+
17+
18+
1419
private int StepIncrement = 1;
1520
public ConversionProgress()
1621
{
1722
InitializeComponent();
23+
DataContext = this;
1824
}
1925

2026

@@ -32,13 +38,20 @@ public void AddMessage(string message, bool isProgress = true)
3238
foreach (string line in lines) {
3339
if (isProgress) {
3440
CurrentProgressMessage = line;
35-
CurrentAction.Content = CurrentProgressMessage;
41+
CurrentAction.Text = CurrentProgressMessage;
3642
ProgressionTracker.Value += StepIncrement;
43+
3744
} else {
38-
CurrentAction.Content = CurrentProgressMessage + " - " + line;
45+
CurrentAction.Text = CurrentProgressMessage + " - " + line;
3946
}
4047
MessageTextArea.AppendText(line + "\r\n");
4148
}
49+
CurrentAction.Focus();
50+
AutomationProperties.SetHelpText(CurrentAction, CurrentProgressMessage);
51+
var peer = UIElementAutomationPeer.CreatePeerForElement(CurrentAction);
52+
if (peer != null) {
53+
peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
54+
}
4255
MessageTextArea.ScrollToEnd();
4356
this.UpdateLayout();
4457
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
@@ -59,11 +72,18 @@ public void InitializeProgress(string message = "", int maximum = 1, int step =
5972
return;
6073
}
6174
CurrentProgressMessage = message;
62-
CurrentAction.Content = CurrentProgressMessage;
75+
CurrentAction.Text = CurrentProgressMessage;
76+
6377
this.MessageTextArea.AppendText((message.EndsWith("\n") ? message : message + "\r\n"));
6478
ProgressionTracker.Maximum = maximum;
6579
StepIncrement = step;
6680
ProgressionTracker.Value = 0;
81+
CurrentAction.Focus();
82+
AutomationProperties.SetHelpText(CurrentAction, CurrentProgressMessage);
83+
var peer = UIElementAutomationPeer.CreatePeerForElement(CurrentAction);
84+
if (peer != null) {
85+
peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
86+
}
6787
this.UpdateLayout();
6888
this.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
6989
}

Common/DaisyAddinWPFLib/CustomControls/ScriptParameter/PathParameter.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
Margin="5,0,5,0"
1717
Width="72"
1818
x:Name="BrowseButton"
19-
Click="BrowseButton_Click" />
19+
Click="BrowseButton_Click"
20+
AutomationProperties.Name="{Binding Path=BrowseLabel}" />
2021
<TextBox TextWrapping="NoWrap"
2122
Text="{Binding Path=ParameterValue, Mode=TwoWay}"
2223
x:Name="PathTextBox"

Common/DaisyAddinWPFLib/CustomControls/ScriptParameter/PathParameter.xaml.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using UserControl = System.Windows.Controls.UserControl;
66
using System.IO;
77
using System;
8+
using System.Windows.Automation;
89

910
namespace Daisy.SaveAsDAISY.WPF.CustomControls
1011
{
@@ -45,6 +46,8 @@ public string ParameterValue {
4546
}
4647
}
4748

49+
public string BrowseLabel { get => "Browse " + ParameterName; }
50+
4851
public string ParameterDescription {
4952
get => BoundParameter != null ? BoundParameter.Description : _parameterDescription;
5053
set => _parameterDescription = value;

Common/DaisyAddinWPFLib/WPFEventsHandler.cs

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,46 @@ public class WPFEventsHandler : Daisy.SaveAsDAISY.Conversion.Events.IConversionE
2121
#region Conversion progress dialog
2222
public void TryInitializeProgress(string message, int maximum = 1, int step = 1)
2323
{
24-
try {
25-
if (DialogInstance == null) {
26-
var test = new Thread(() =>
27-
{
28-
DialogInstance = new ConversionProgress();
29-
DialogInstance.Closed += Dialog_Closed;
30-
if (cancelButtonClicked != null) {
31-
DialogInstance.setCancelClickListener(cancelButtonClicked);
32-
}
33-
DialogInstance.Show();
34-
35-
DialogInstance.Dispatcher.Invoke(() => DialogInstance.InitializeProgress(message, maximum, step));
36-
while(DialogInstance != null) {
37-
Dispatcher.Run();
24+
lock (DialogInstance) {
25+
try {
26+
if (DialogInstance == null) {
27+
var test = new Thread(() =>
28+
{
29+
DialogInstance = new ConversionProgress();
30+
DialogInstance.Closed += Dialog_Closed;
31+
if (cancelButtonClicked != null) {
32+
DialogInstance.setCancelClickListener(cancelButtonClicked);
33+
}
34+
DialogInstance.Show();
35+
36+
DialogInstance.Dispatcher.Invoke(() => DialogInstance.InitializeProgress(message, maximum, step));
37+
while (DialogInstance != null) {
38+
Dispatcher.Run();
39+
}
40+
41+
});
42+
test.SetApartmentState(ApartmentState.STA);
43+
test.Start();
44+
int timeout = 2;
45+
while(DialogInstance == null && timeout > 0) {
46+
Thread.Sleep(1000); // give some time to show the dialog
47+
timeout--;
3848
}
49+
3950

40-
});
41-
test.SetApartmentState(ApartmentState.STA);
42-
test.Start();
43-
Thread.Sleep(500); // give some time to show the dialog
51+
} else {
52+
DialogInstance.Dispatcher.Invoke(() => {
53+
DialogInstance.Activate();
54+
DialogInstance.InitializeProgress(message, maximum, step);
55+
});
56+
}
4457

45-
} else {
46-
DialogInstance.Dispatcher.Invoke(() => {
47-
DialogInstance.Activate();
48-
DialogInstance.InitializeProgress(message, maximum, step);
49-
});
5058
}
51-
52-
}
53-
catch (Exception e) {
54-
AddinLogger.Error("Unable to show message in progress dialog: " + message + " " + e.Message);
59+
catch (Exception e) {
60+
AddinLogger.Error("Unable to show message in progress dialog: " + message + " " + e.Message);
61+
}
5562
}
63+
5664
}
5765

5866
private event CancelClickListener cancelButtonClicked = null;
@@ -91,7 +99,11 @@ private void TryShowMessage(string message, bool isProgress = false)
9199
});
92100
test.SetApartmentState(ApartmentState.STA);
93101
test.Start();
94-
Thread.Sleep(500); // give some time to show the dialog
102+
int timeout = 20;
103+
while (DialogInstance == null && timeout > 0) {
104+
Thread.Sleep(100); // give some time to show the dialog
105+
timeout--;
106+
}
95107
} else {
96108
DialogInstance.Dispatcher.Invoke(() => {
97109
DialogInstance.Activate();
@@ -129,6 +141,9 @@ public void onDocumentPreprocessingStart(string inputPath)
129141

130142
public void onPreprocessingCancel()
131143
{
144+
if( DialogInstance == null ) {
145+
return;
146+
}
132147
TryShowMessage("Preprocessing canceled ");
133148
TryClosingDialog(3000);
134149
}
@@ -276,6 +291,9 @@ public void onPostProcessingSuccess(ConversionParameters conversion)
276291

277292
public void onConversionCanceled()
278293
{
294+
if(DialogInstance == null) {
295+
return;
296+
}
279297
TryShowMessage("Canceling conversion");
280298
}
281299

@@ -366,7 +384,7 @@ public void onPostProcessingError(Exception errors)
366384
public void onConversionSuccess()
367385
{
368386
TryShowMessage("Successfull conversion", false);
369-
TryClosingDialog(3000);
387+
//TryClosingDialog(3000);
370388
}
371389

372390
public void onPreprocessingWarning(string message)

0 commit comments

Comments
 (0)