Merge pull request #417 from yolpsoftware/master

Accessibility round 2
This commit is contained in:
Damian Büchel 2022-06-01 12:17:30 +02:00 committed by GitHub
commit 7f53b3ddf4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 452 additions and 63 deletions

View file

@ -37,7 +37,10 @@ namespace SafeExamBrowser.I18n.Contracts
BrowserWindow_MenuButton,
BrowserWindow_ReloadButton,
BrowserWindow_UrlTextBox,
BrowserWindow_ZoomLevelReset,
BrowserWindow_ZoomMenuItem,
BrowserWindow_ZoomMenuMinus,
BrowserWindow_ZoomMenuPlus,
Build,
ExamSelectionDialog_Cancel,
ExamSelectionDialog_Message,

View file

@ -69,9 +69,18 @@
<Entry key="BrowserWindow_UrlTextBox">
URL eingeben
</Entry>
<Entry key="BrowserWindow_ZoomLevelReset">
Seiten-Zoom bei %%ZOOM%% %. Klicken zum Zurücksetzen.
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
Seiten-Zoom
</Entry>
<Entry key="BrowserWindow_ZoomMenuPlus">
Zoom vergrössern
</Entry>
<Entry key="BrowserWindow_ZoomMenuMinus">
Zoom verkleinern
</Entry>
<Entry key="Build">
Build
</Entry>

View file

@ -69,9 +69,18 @@
<Entry key="BrowserWindow_UrlTextBox">
Enter URL
</Entry>
<Entry key="BrowserWindow_ZoomLevelReset">
Page Zoom at %%ZOOM%% %. Click to Reset.
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
Page Zoom
</Entry>
<Entry key="BrowserWindow_ZoomMenuPlus">
Increase Page Zoom
</Entry>
<Entry key="BrowserWindow_ZoomMenuMinus">
Decrease Page Zoom
</Entry>
<Entry key="Build">
Build
</Entry>

View file

@ -69,9 +69,18 @@
<Entry key="BrowserWindow_UrlTextBox">
Entrer l'URL
</Entry>
<Entry key="BrowserWindow_ZoomLevelReset">
Zoom de la page à %%ZOOM%% %. Cliquer pour réinitialiser.
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
Zoom de la page
</Entry>
<Entry key="BrowserWindow_ZoomMenuPlus">
Augmenter zoom
</Entry>
<Entry key="BrowserWindow_ZoomMenuMinus">
Diminuer zoom
</Entry>
<Entry key="Build">
Build
</Entry>

View file

@ -69,9 +69,18 @@
<Entry key="BrowserWindow_UrlTextBox">
Inserisci URL
</Entry>
<Entry key="BrowserWindow_ZoomLevelReset">
Zoom della pagina al %%ZOOM%% %. Fare clic per ripristinare.
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
Zoom della pagina
</Entry>
<Entry key="BrowserWindow_ZoomMenuPlus">
Aumentare zoom
</Entry>
<Entry key="BrowserWindow_ZoomMenuMinus">
Diminuire zoom
</Entry>
<Entry key="Build">
Build
</Entry>

View file

@ -66,9 +66,18 @@
<Entry key="BrowserWindow_UrlTextBox">
输入网址
</Entry>
<Entry key="BrowserWindow_ZoomLevelReset">
页面缩放为 %%ZOOM%% %。点击重置。
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
页面缩放
</Entry>
<Entry key="BrowserWindow_ZoomMenuPlus">
增加页面缩放
</Entry>
<Entry key="BrowserWindow_ZoomMenuMinus">
减少页面缩放
</Entry>
<Entry key="Build">
生成
</Entry>

View file

@ -121,6 +121,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
{
Button.ToolTip = text;
Text.Text = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength)

View file

@ -87,6 +87,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Text.Text = tooltip;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
this.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)

View file

@ -88,6 +88,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name);
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, Button.ToolTip as string);
}
WirelessNetworksStackPanel.Children.Add(button);

View file

@ -8,7 +8,6 @@
using System;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
@ -102,7 +101,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
Button.ToolTip = tooltip;
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
AutomationProperties.SetHelpText(this, Button.ToolTip as string);
this.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)

View file

@ -11,6 +11,7 @@ using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
@ -196,7 +197,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
public void UpdateZoomLevel(double value)
{
Dispatcher.Invoke(() => ZoomLevel.Text = $"{value}%");
Dispatcher.Invoke(() =>
{
ZoomLevel.Text = $"{value}%";
var zoomButtonHelpText = this.text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0"));
ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, zoomButtonHelpText);
});
}
private void BrowserWindow_Closing(object sender, CancelEventArgs e)
@ -297,6 +303,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
}
}
}
if (e.Key == Key.Escape && MenuPopup.IsOpen)
{
MenuPopup.IsOpen = false;
MenuButton.Focus();
}
}
/// <summary>
@ -586,9 +598,13 @@ if (typeof __SEB_focusElement === 'undefined') {
private void LoadText()
{
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem));
FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_FindMenuItem));
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus));
ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus));
ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton));
BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton));
ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton));

View file

@ -5,7 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250" IsTabStop="True">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>

View file

@ -16,7 +16,7 @@
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}" KeyUp="Popup_KeyUp">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />

View file

@ -41,7 +41,14 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
InitializeLayouts();
keyboard.LayoutChanged += Keyboard_LayoutChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.Click += (o, args) =>
{
Popup.IsOpen = !Popup.IsOpen;
this.Dispatcher.BeginInvoke((System.Action) (() =>
{
LayoutsStackPanel.Children[0].Focus();
}));
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
@ -90,5 +97,14 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
Text.Text = layout.CultureName;
Button.ToolTip = tooltip;
}
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter || e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View file

@ -121,6 +121,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
Button.ToolTip = text;
Text.Text = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength)

View file

@ -87,6 +87,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Text.Text = tooltip;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
this.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)

View file

@ -16,7 +16,7 @@
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}" KeyDown="Popup_KeyDown">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" />
@ -24,7 +24,7 @@
<Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="60">
<ContentControl x:Name="PopupIcon" />
</Button>
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True"
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True" KeyDown="Volume_KeyDown"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted">
<Slider.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" CenterX="0" CenterY="0"/>
@ -37,7 +37,7 @@
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60">
<ContentControl x:Name="ButtonIcon" />
<ContentControl x:Name="ButtonIcon" Focusable="False" />
</Button>
</Grid>
</UserControl>

View file

@ -60,6 +60,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
Volume.Focus();
};
Popup.Closed += (o, args) =>
@ -167,5 +168,23 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
return IconResourceLoader.Load(resource);
}
private void Popup_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
private void Volume_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View file

@ -11,7 +11,7 @@
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" HorizontalAlignment="Center" VerticalAlignment="Top" />
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" Focusable="True" AutomationProperties.HelpText="{Binding Path=Time}" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" HorizontalAlignment="Center" VerticalAlignment="Top" Focusable="True" AutomationProperties.HelpText="{Binding Path=Date}" />
</Grid>
</UserControl>

View file

@ -14,24 +14,22 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<StackPanel Grid.Column="2" VerticalAlignment="Center">
<TextBlock x:Name="CultureNameTextBlock" HorizontalAlignment="Left" Margin="5,0,10,0" TextDecorations="Underline" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal">
<fa:ImageAwesome Foreground="Gray" Height="10" Icon="KeyboardOutline" Margin="5,0" />
<TextBlock x:Name="LayoutNameTextBlock" Foreground="Gray" HorizontalAlignment="Left" Margin="0,0,10,0" VerticalAlignment="Center" />
</StackPanel>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<StackPanel Grid.Column="2" VerticalAlignment="Center">
<TextBlock x:Name="CultureNameTextBlock" HorizontalAlignment="Left" Margin="5,0,10,0" TextDecorations="Underline" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal">
<fa:ImageAwesome Foreground="Gray" Height="10" Icon="KeyboardOutline" Margin="5,0" />
<TextBlock x:Name="LayoutNameTextBlock" Foreground="Gray" HorizontalAlignment="Left" Margin="0,0,10,0" VerticalAlignment="Center" />
</StackPanel>
</Grid>
</Button>
</Grid>
</StackPanel>
</Grid>
</Button>
</UserControl>

View file

@ -43,6 +43,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
CultureCodeTextBlock.Text = layout.CultureCode;
CultureNameTextBlock.Text = layout.CultureName;
LayoutNameTextBlock.Text = layout.LayoutName;
System.Windows.Automation.AutomationProperties.SetHelpText(Button, layout.LayoutName);
}
}
}

View file

@ -16,7 +16,7 @@
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}" KeyUp="Popup_KeyUp">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />

View file

@ -45,7 +45,14 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
InitializeLayouts();
keyboard.LayoutChanged += Keyboard_LayoutChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.Click += (o, args) =>
{
Popup.IsOpen = !Popup.IsOpen;
Task.Delay(200).ContinueWith(_ => this.Dispatcher.BeginInvoke((System.Action) (() =>
{
((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus();
})));
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
@ -114,5 +121,14 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
LayoutCultureCode.Text = layout.CultureCode;
Button.ToolTip = tooltip;
}
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter || e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View file

@ -88,6 +88,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name);
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, Button.ToolTip as string);
}
WirelessNetworksStackPanel.Children.Add(button);

View file

@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40" Focusable="True" IsTabStop="True">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>

View file

@ -101,6 +101,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
Button.ToolTip = tooltip;
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
this.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)

View file

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
x:Name="root"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
@ -15,6 +16,7 @@
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Click="Button_Click" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch"
Template="{StaticResource TaskbarButton}" VerticalAlignment="Stretch" />
Template="{StaticResource TaskbarButton}" VerticalAlignment="Stretch"
AutomationProperties.Name="{Binding Path=(AutomationProperties.Name), ElementName=root}" />
</Grid>
</UserControl>

View file

@ -62,15 +62,15 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="ZoomText" HorizontalAlignment="Left" Margin="10,0" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="ZoomInButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<Button Grid.Column="1" x:Name="ZoomInButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="30">
<fa:ImageAwesome Icon="SearchPlus" />
</Button>
<Button Grid.Column="2" x:Name="ZoomResetButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<Button Grid.Column="2" x:Name="ZoomResetButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="31">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="ZoomLevel" />
</Viewbox>
</Button>
<Button Grid.Column="3" x:Name="ZoomOutButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<Button Grid.Column="3" x:Name="ZoomOutButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="32">
<fa:ImageAwesome Icon="SearchMinus" />
</Button>
</Grid>
@ -80,7 +80,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="FindMenuText" HorizontalAlignment="Left" Margin="10,0" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="FindMenuButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<Button Grid.Column="1" x:Name="FindMenuButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="33">
<fa:ImageAwesome Icon="Search" />
</Button>
</Grid>
@ -90,7 +90,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="DeveloperConsoleText" HorizontalAlignment="Left" Margin="10,0" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="DeveloperConsoleButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<Button Grid.Column="1" x:Name="DeveloperConsoleButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="34">
<fa:ImageAwesome Icon="Wrench" />
</Button>
</Grid>

View file

@ -8,6 +8,7 @@
using System;
using System.ComponentModel;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;
@ -35,12 +36,15 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
private const string CLEAR_FIND_TERM = "thisisahacktoclearthesearchresultsasitappearsthatthereisnosuchfunctionalityincef";
private readonly bool isMainWindow;
private readonly ILogger logger;
private readonly BrowserSettings settings;
private readonly IText text;
private readonly ILogger logger;
private readonly IBrowserControl browserControl;
private WindowClosedEventHandler closed;
private WindowClosingEventHandler closing;
private bool browserControlGetsFocusFromTaskbar = false;
private IInputElement tabKeyDownFocusElement = null;
private WindowSettings WindowSettings
{
@ -56,12 +60,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
public event ActionRequestedEventHandler DeveloperConsoleRequested;
public event FindRequestedEventHandler FindRequested;
public event ActionRequestedEventHandler ForwardNavigationRequested;
public event LoseFocusRequestedEventHandler LoseFocusRequested { add { } remove { } }
public event ActionRequestedEventHandler HomeNavigationRequested;
public event ActionRequestedEventHandler ReloadRequested;
public event ActionRequestedEventHandler ZoomInRequested;
public event ActionRequestedEventHandler ZoomOutRequested;
public event ActionRequestedEventHandler ZoomResetRequested;
public event LoseFocusRequestedEventHandler LoseFocusRequested;
event WindowClosedEventHandler IWindow.Closed
{
@ -78,9 +82,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
internal BrowserWindow(IBrowserControl browserControl, BrowserSettings settings, bool isMainWindow, IText text, ILogger logger)
{
this.isMainWindow = isMainWindow;
this.logger = logger;
this.settings = settings;
this.text = text;
this.logger = logger;
this.browserControl = browserControl;
InitializeComponent();
InitializeBrowserWindow(browserControl);
@ -191,7 +196,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
public void UpdateZoomLevel(double value)
{
Dispatcher.Invoke(() => ZoomLevel.Text = $"{value}%");
Dispatcher.Invoke(() =>
{
ZoomLevel.Text = $"{value}%";
var zoomButtonHelpText = this.text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0"));
ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, zoomButtonHelpText);
});
}
private void BrowserWindow_Closing(object sender, CancelEventArgs e)
@ -206,6 +216,33 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
}
}
private void BrowserWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab)
{
var hasShift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (Toolbar.IsKeyboardFocusWithin && hasShift)
{
var firstActiveElementInToolbar = Toolbar.PredictFocus(FocusNavigationDirection.Right);
if (firstActiveElementInToolbar is System.Windows.UIElement)
{
var control = firstActiveElementInToolbar as System.Windows.UIElement;
if (control.IsKeyboardFocusWithin)
{
this.LoseFocusRequested?.Invoke(false);
e.Handled = true;
}
}
}
tabKeyDownFocusElement = FocusManager.GetFocusedElement(this);
}
else
{
tabKeyDownFocusElement = null;
}
}
private void BrowserWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.F5)
@ -222,6 +259,71 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
ShowFindbar();
}
if (e.Key == Key.Tab)
{
var hasCtrl = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
var hasShift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
if (BrowserControlHost.IsFocused && hasCtrl)
{
if (Findbar.Visibility == Visibility.Hidden || hasShift)
{
Toolbar.Focus();
}
else if (Toolbar.Visibility == Visibility.Hidden)
{
Findbar.Focus();
}
}
else if (MenuPopup.IsKeyboardFocusWithin)
{
var focusedElement = FocusManager.GetFocusedElement(this);
var focusedControl = focusedElement as System.Windows.Controls.Control;
var prevFocusedControl = tabKeyDownFocusElement as System.Windows.Controls.Control;
if (focusedControl != null && prevFocusedControl != null)
{
//var commonAncestor = focusedControl.FindCommonVisualAncestor(prevFocusedControl);
//var nextTab = GetNextTab(MenuPopup, this, true);
if (!hasShift && focusedControl.TabIndex < prevFocusedControl.TabIndex)
{
MenuPopup.IsOpen = false;
FocusBrowser();
}
else if (hasShift && focusedControl.TabIndex > prevFocusedControl.TabIndex)
{
MenuPopup.IsOpen = false;
MenuButton.Focus();
}
}
}
}
if (e.Key == Key.Escape && MenuPopup.IsOpen)
{
MenuPopup.IsOpen = false;
MenuButton.Focus();
}
}
/// <summary>
/// Get next tab order element. Copied from https://stackoverflow.com/questions/5756448/in-wpf-how-can-i-get-the-next-control-in-the-tab-order
/// </summary>
/// <param name="e">The element to get next tab order</param>
/// <param name="container">The container element owning 'e'. Make sure this is a container of 'e'.</param>
/// <param name="goDownOnly">True if search only itself and inside of 'container'; otherwise false.
/// If true and next tab order element is outside of 'container', result in null.</param>
/// <returns>Next tab order element or null if not found</returns>
public DependencyObject GetNextTab(DependencyObject e, DependencyObject container, bool goDownOnly)
{
var navigation = typeof(FrameworkElement)
.GetProperty("KeyboardNavigation", BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(null);
var method = navigation
.GetType()
.GetMethod("GetNextTab", BindingFlags.NonPublic | BindingFlags.Instance);
return method.Invoke(navigation, new object[] { e, container, goDownOnly }) as DependencyObject;
}
private void BrowserWindow_Loaded(object sender, RoutedEventArgs e)
@ -327,10 +429,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
HomeButton.Click += (o, args) => HomeNavigationRequested?.Invoke();
Loaded += BrowserWindow_Loaded;
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
MenuButton.Click += MenuButton_Click;
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
KeyDown += BrowserWindow_KeyDown;
KeyUp += BrowserWindow_KeyUp;
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
@ -345,6 +448,48 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
ZoomInButton.Click += (o, args) => ZoomInRequested?.Invoke();
ZoomOutButton.Click += (o, args) => ZoomOutRequested?.Invoke();
ZoomResetButton.Click += (o, args) => ZoomResetRequested?.Invoke();
BrowserControlHost.GotKeyboardFocus += BrowserControlHost_GotKeyboardFocus;
}
private void MenuButton_Click(object sender, RoutedEventArgs e)
{
MenuPopup.IsOpen = !MenuPopup.IsOpen;
ZoomInButton.Focus();
}
private void BrowserControlHost_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var forward = !this.browserControlGetsFocusFromTaskbar;
// focus the first / last element on the page
var javascript = @"
if (typeof __SEB_focusElement === 'undefined') {
__SEB_focusElement = function (forward) {
var items = [].map
.call(document.body.querySelectorAll(['input', 'select', 'a[href]', 'textarea', 'button', '[tabindex]']), function(el, i) { return { el, i } })
.filter(function(e) { return e.el.tabIndex >= 0 && !e.el.disabled && e.el.offsetParent; })
.sort(function(a,b) { return a.el.tabIndex === b.el.tabIndex ? a.i - b.i : (a.el.tabIndex || 9E9) - (b.el.tabIndex || 9E9); })
var item = items[forward ? 1 : items.length - 1];
if (item && item.focus && typeof item.focus !== 'function')
throw ('item.focus is not a function, ' + typeof item.focus)
setTimeout(function () { item && item.focus && item.focus(); }, 20);
}
}";
this.browserControl.ExecuteJavascript(javascript, result =>
{
if (!result.Success)
{
logger.Error($"Javascript error {result.Message}!");
}
});
this.browserControl.ExecuteJavascript("__SEB_focusElement(" + forward.ToString().ToLower() + ")", result =>
{
if (!result.Success)
{
logger.Error($"Javascript error {result.Message}!");
}
});
}
private void ApplySettings()
@ -457,29 +602,65 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
private void LoadText()
{
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem));
FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_FindMenuItem));
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus));
ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus));
ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton));
BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton));
ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton));
DownloadsButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_DownloadsButton));
HomeButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_HomeButton));
MenuButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_MenuButton));
UrlTextBox.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_UrlTextBox));
}
public void FocusToolbar(bool forward)
{
throw new NotImplementedException();
this.Dispatcher.BeginInvoke((Action) (async () =>
{
this.Activate();
await Task.Delay(50);
// focus all elements in the toolbar, such that the last element that is enabled gets focus
var buttons = new System.Windows.Controls.Control[] { ForwardButton, BackwardButton, ReloadButton, UrlTextBox, MenuButton, };
for (var i = forward ? 0 : buttons.Length - 1; i >= 0 && i < buttons.Length; i += forward ? 1 : -1)
{
if (buttons[i].IsEnabled && buttons[i].Visibility == Visibility.Visible)
{
buttons[i].Focus();
break;
}
}
}));
}
public void FocusBrowser()
{
throw new NotImplementedException();
}
this.Dispatcher.BeginInvoke((Action) (async () =>
{
this.FocusToolbar(false);
await Task.Delay(100);
public void Debug()
{
throw new NotImplementedException();
this.browserControlGetsFocusFromTaskbar = true;
var focusedElement = FocusManager.GetFocusedElement(this) as UIElement;
focusedElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
await Task.Delay(150);
this.browserControlGetsFocusFromTaskbar = false;
}));
}
public void FocusAddressBar()
{
this.UrlTextBox.Focus();
this.Dispatcher.BeginInvoke((Action) (async () =>
{
this.UrlTextBox.Focus();
}));
}
}
}

View file

@ -6,6 +6,7 @@
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Title="Taskbar" Background="{DynamicResource BackgroundBrush}" Height="60" FontSize="16" Width="750" WindowStyle="None"
KeyDown="Window_KeyDown" KeyUp="Window_KeyUp"
Topmost="True" ResizeMode="NoResize" Icon="../Images/SafeExamBrowser.ico">
<Window.Resources>
<ResourceDictionary>

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.ComponentModel;
using System.Windows;
using SafeExamBrowser.Browser.Contracts.Events;
@ -19,8 +20,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
internal partial class Taskbar : Window, ITaskbar
{
private readonly ILogger logger;
private bool allowClose;
private readonly ILogger logger;
private bool isQuitButtonFocusedAtKeyDown;
private bool isFirstChildFocusedAtKeyDown;
public bool ShowClock
{
@ -32,7 +35,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
set { Dispatcher.Invoke(() => QuitButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed); }
}
public event LoseFocusRequestedEventHandler LoseFocusRequested { add { } remove { } }
public event LoseFocusRequestedEventHandler LoseFocusRequested;
public event QuitButtonClickedEventHandler QuitButtonClicked;
internal Taskbar(ILogger logger)
@ -81,16 +84,19 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
public void Focus(bool fromTop)
{
Activate();
Dispatcher.BeginInvoke((Action) (() =>
{
Activate();
if (fromTop)
{
ApplicationStackPanel.Children[0].Focus();
}
else
{
QuitButton.Focus();
}
if (fromTop)
{
ApplicationStackPanel.Children[0].Focus();
}
else
{
QuitButton.Focus();
}
}));
}
public int GetAbsoluteHeight()
@ -143,7 +149,9 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
Dispatcher.Invoke(() =>
{
QuitButton.ToolTip = text.Get(TextKey.Shell_QuitButton);
var txt = text.Get(TextKey.Shell_QuitButton);
QuitButton.ToolTip = txt;
QuitButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, txt);
});
}
@ -185,5 +193,82 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
e.Cancel = true;
}
}
private void Window_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
this.isQuitButtonFocusedAtKeyDown = this.QuitButton.IsKeyboardFocusWithin;
this.isFirstChildFocusedAtKeyDown = this.ApplicationStackPanel.Children[0].IsKeyboardFocusWithin;
}
private void Window_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Tab)
{
var shift = System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift);
if (!shift && this.ApplicationStackPanel.Children[0].IsKeyboardFocusWithin && this.isQuitButtonFocusedAtKeyDown)
{
this.LoseFocusRequested?.Invoke(true);
e.Handled = true;
}
else if (shift && this.QuitButton.IsKeyboardFocusWithin && this.isFirstChildFocusedAtKeyDown)
{
this.LoseFocusRequested?.Invoke(false);
e.Handled = true;
}
}
this.isQuitButtonFocusedAtKeyDown = false;
this.isFirstChildFocusedAtKeyDown = false;
}
void ITaskbar.Focus(bool forward)
{
this.Dispatcher.BeginInvoke((Action) (() =>
{
base.Activate();
if (forward)
{
this.SetFocusWithin(this.ApplicationStackPanel.Children[0]);
}
else
{
this.QuitButton.Focus();
}
}));
}
private bool SetFocusWithin(UIElement uIElement)
{
if (uIElement.Focusable)
{
uIElement.Focus();
return true;
}
if (uIElement is System.Windows.Controls.Panel)
{
var panel = uIElement as System.Windows.Controls.Panel;
for (var i = 0; i < panel.Children.Count; i++)
{
if (this.SetFocusWithin(panel.Children[i]))
{
return true;
}
}
return false;
}
else if (uIElement is System.Windows.Controls.ContentControl)
{
var control = uIElement as System.Windows.Controls.ContentControl;
var content = control.Content as UIElement;
if (content != null)
{
return this.SetFocusWithin(content);
}
}
return false;
}
}
}