This commit is contained in:
Damian Büchel 2023-03-07 23:13:02 +01:00
commit 66bd6a2d90
48 changed files with 683 additions and 103 deletions

View file

@ -26,6 +26,7 @@ namespace SafeExamBrowser.I18n.Contracts
Browser_PrintNotAllowed, Browser_PrintNotAllowed,
Browser_Tooltip, Browser_Tooltip,
BrowserWindow_BackwardButton, BrowserWindow_BackwardButton,
BrowserWindow_CloseButton,
BrowserWindow_DeveloperConsoleMenuItem, BrowserWindow_DeveloperConsoleMenuItem,
BrowserWindow_Downloading, BrowserWindow_Downloading,
BrowserWindow_DownloadCancelled, BrowserWindow_DownloadCancelled,
@ -37,6 +38,9 @@ namespace SafeExamBrowser.I18n.Contracts
BrowserWindow_HomeButton, BrowserWindow_HomeButton,
BrowserWindow_MenuButton, BrowserWindow_MenuButton,
BrowserWindow_ReloadButton, BrowserWindow_ReloadButton,
BrowserWindow_SearchNext,
BrowserWindow_SearchPrevious,
BrowserWindow_SearchTextBox,
BrowserWindow_UrlTextBox, BrowserWindow_UrlTextBox,
BrowserWindow_ZoomLevelReset, BrowserWindow_ZoomLevelReset,
BrowserWindow_ZoomMenuItem, BrowserWindow_ZoomMenuItem,

View file

@ -31,7 +31,7 @@
Drucken ist nicht erlaubt gemäss der aktiven Konfiguration. Drucken ist nicht erlaubt gemäss der aktiven Konfiguration.
</Entry> </Entry>
<Entry key="Browser_Tooltip"> <Entry key="Browser_Tooltip">
Browser Applikation Geöffnete Websites
</Entry> </Entry>
<Entry key="BrowserWindow_DeveloperConsoleMenuItem"> <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
Entwickler-Konsole Entwickler-Konsole
@ -69,6 +69,9 @@
<Entry key="BrowserWindow_MenuButton"> <Entry key="BrowserWindow_MenuButton">
Menü Menü
</Entry> </Entry>
<Entry key="BrowserWindow_CloseButton">
Schliessen
</Entry>
<Entry key="BrowserWindow_UrlTextBox"> <Entry key="BrowserWindow_UrlTextBox">
URL eingeben URL eingeben
</Entry> </Entry>
@ -84,6 +87,15 @@
<Entry key="BrowserWindow_ZoomMenuMinus"> <Entry key="BrowserWindow_ZoomMenuMinus">
Zoom verkleinern Zoom verkleinern
</Entry> </Entry>
<Entry key="BrowserWindow_SearchTextBox">
Text eingeben zum Suchen
</Entry>
<Entry key="BrowserWindow_SearchNext">
Nächstes Suchresultat
</Entry>
<Entry key="BrowserWindow_SearchPrevious">
Vorheriges Suchresultat
</Entry>
<Entry key="Build"> <Entry key="Build">
Build Build
</Entry> </Entry>

View file

@ -31,7 +31,7 @@
Printing is not allowed according to the current configuration. Printing is not allowed according to the current configuration.
</Entry> </Entry>
<Entry key="Browser_Tooltip"> <Entry key="Browser_Tooltip">
Browser Application Open Webpages
</Entry> </Entry>
<Entry key="BrowserWindow_DeveloperConsoleMenuItem"> <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
Developer Console Developer Console
@ -69,6 +69,9 @@
<Entry key="BrowserWindow_MenuButton"> <Entry key="BrowserWindow_MenuButton">
Menu Menu
</Entry> </Entry>
<Entry key="BrowserWindow_CloseButton">
Close
</Entry>
<Entry key="BrowserWindow_UrlTextBox"> <Entry key="BrowserWindow_UrlTextBox">
Enter URL Enter URL
</Entry> </Entry>
@ -84,6 +87,15 @@
<Entry key="BrowserWindow_ZoomMenuMinus"> <Entry key="BrowserWindow_ZoomMenuMinus">
Decrease Page Zoom Decrease Page Zoom
</Entry> </Entry>
<Entry key="BrowserWindow_SearchTextBox">
Enter text to search
</Entry>
<Entry key="BrowserWindow_SearchNext">
Next search result
</Entry>
<Entry key="BrowserWindow_SearchPrevious">
Previous search result
</Entry>
<Entry key="Build"> <Entry key="Build">
Build Build
</Entry> </Entry>

View file

@ -34,7 +34,7 @@
Application du navigateur Application du navigateur
</Entry> </Entry>
<Entry key="BrowserWindow_DeveloperConsoleMenuItem"> <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
Console de développement Sites web ouverts
</Entry> </Entry>
<Entry key="BrowserWindow_Downloading"> <Entry key="BrowserWindow_Downloading">
Chargement… Chargement…
@ -69,6 +69,9 @@
<Entry key="BrowserWindow_MenuButton"> <Entry key="BrowserWindow_MenuButton">
Menu Menu
</Entry> </Entry>
<Entry key="BrowserWindow_CloseButton">
Fermer
</Entry>
<Entry key="BrowserWindow_UrlTextBox"> <Entry key="BrowserWindow_UrlTextBox">
Entrer l'URL Entrer l'URL
</Entry> </Entry>
@ -84,6 +87,15 @@
<Entry key="BrowserWindow_ZoomMenuMinus"> <Entry key="BrowserWindow_ZoomMenuMinus">
Diminuer zoom Diminuer zoom
</Entry> </Entry>
<Entry key="BrowserWindow_SearchTextBox">
Saisir du texte pour rechercher
</Entry>
<Entry key="BrowserWindow_SearchNext">
Résultat de recherche prochain
</Entry>
<Entry key="BrowserWindow_SearchPrevious">
Résultat de recherche précédent
</Entry>
<Entry key="Build"> <Entry key="Build">
Build Build
</Entry> </Entry>

View file

@ -34,7 +34,7 @@
Applicazione browser Applicazione browser
</Entry> </Entry>
<Entry key="BrowserWindow_DeveloperConsoleMenuItem"> <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
Console di sviluppo Siti web aperti
</Entry> </Entry>
<Entry key="BrowserWindow_Downloading"> <Entry key="BrowserWindow_Downloading">
Download in corso ... Download in corso ...
@ -69,6 +69,9 @@
<Entry key="BrowserWindow_MenuButton"> <Entry key="BrowserWindow_MenuButton">
Menu Menu
</Entry> </Entry>
<Entry key="BrowserWindow_CloseButton">
Chiudere
</Entry>
<Entry key="BrowserWindow_UrlTextBox"> <Entry key="BrowserWindow_UrlTextBox">
Inserisci URL Inserisci URL
</Entry> </Entry>
@ -84,6 +87,15 @@
<Entry key="BrowserWindow_ZoomMenuMinus"> <Entry key="BrowserWindow_ZoomMenuMinus">
Diminuire zoom Diminuire zoom
</Entry> </Entry>
<Entry key="BrowserWindow_SearchTextBox">
Immettere il testo da cercare
</Entry>
<Entry key="BrowserWindow_SearchNext">
Risultato della ricerca successiva
</Entry>
<Entry key="BrowserWindow_SearchPrevious">
Risultato della ricerca precedente
</Entry>
<Entry key="Build"> <Entry key="Build">
Build Build
</Entry> </Entry>

View file

@ -34,7 +34,7 @@
根据当前配置不允许打印。 根据当前配置不允许打印。
</Entry> </Entry>
<Entry key="Browser_Tooltip"> <Entry key="Browser_Tooltip">
浏览器应用程序 开放网站
</Entry> </Entry>
<Entry key="BrowserWindow_DeveloperConsoleMenuItem"> <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
开发者控制台 开发者控制台
@ -66,6 +66,9 @@
<Entry key="BrowserWindow_MenuButton"> <Entry key="BrowserWindow_MenuButton">
菜单 菜单
</Entry> </Entry>
<Entry key="BrowserWindow_CloseButton">
关闭
</Entry>
<Entry key="BrowserWindow_UrlTextBox"> <Entry key="BrowserWindow_UrlTextBox">
输入网址 输入网址
</Entry> </Entry>
@ -81,6 +84,15 @@
<Entry key="BrowserWindow_ZoomMenuMinus"> <Entry key="BrowserWindow_ZoomMenuMinus">
减少页面缩放 减少页面缩放
</Entry> </Entry>
<Entry key="BrowserWindow_SearchTextBox">
输入文本进行搜索
</Entry>
<Entry key="BrowserWindow_SearchNext">
下一个搜索结果
</Entry>
<Entry key="BrowserWindow_SearchPrevious">
前一个搜索结果
</Entry>
<Entry key="Build"> <Entry key="Build">
生成 生成
</Entry> </Entry>

View file

@ -35,7 +35,9 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
Icon.Content = IconResourceLoader.Load(window?.Icon ?? application.Icon); Icon.Content = IconResourceLoader.Load(window?.Icon ?? application.Icon);
Text.Text = window?.Title ?? application.Name; Text.Text = window?.Title ?? application.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty); Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = window?.Title ?? application.Tooltip; var tooltip = window?.Title ?? application.Tooltip;
Button.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(Button, tooltip);
if (window != null) if (window != null)
{ {

View file

@ -21,7 +21,7 @@
<TextBlock x:Name="AudioDeviceName" Foreground="White" Margin="5" TextAlignment="Center" /> <TextBlock x:Name="AudioDeviceName" Foreground="White" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="40"> <StackPanel Orientation="Horizontal" Height="40">
<Button x:Name="MuteButton" Background="Transparent" Foreground="White" Padding="5" Template="{StaticResource ActionCenterButton}" Width="40"> <Button x:Name="MuteButton" Background="Transparent" Foreground="White" Padding="5" Template="{StaticResource ActionCenterButton}" Width="40">
<ContentControl x:Name="PopupIcon" /> <ContentControl x:Name="PopupIcon" Focusable="False" />
</Button> </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"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="250" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted" /> IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="250" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted" />

View file

@ -49,13 +49,39 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
audio.VolumeChanged += Audio_VolumeChanged; audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click; MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") }; MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_NoDevice.xaml") }; NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_NoDevice.xaml") };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
if (audio.HasOutputDevice) if (audio.HasOutputDevice)
@ -114,6 +140,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
this.muted = muted; this.muted = muted;
Button.ToolTip = info; Button.ToolTip = info;
System.Windows.Automation.AutomationProperties.SetName(Button, info);
Text.Text = info; Text.Text = info;
Volume.ValueChanged -= Volume_ValueChanged; Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100); Volume.Value = Math.Round(volume * 100);
@ -121,14 +148,18 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
if (muted) if (muted)
{ {
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon); ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
PopupIcon.Content = IconResourceLoader.Load(MutedIcon); PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
} }
else else
{ {
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
ButtonIcon.Content = LoadIcon(volume); ButtonIcon.Content = LoadIcon(volume);
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
PopupIcon.Content = LoadIcon(volume); PopupIcon.Content = LoadIcon(volume);
} }
} }

View file

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

View file

@ -6,7 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
@ -49,10 +51,36 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
LayoutsStackPanel.Children[0].Focus(); LayoutsStackPanel.Children[0].Focus();
})); }));
}; };
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
} }
private void Keyboard_LayoutChanged(IKeyboardLayout layout) private void Keyboard_LayoutChanged(IKeyboardLayout layout)
@ -96,6 +124,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
Text.Text = layout.CultureName; Text.Text = layout.CultureName;
Button.ToolTip = tooltip; Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
} }
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View file

@ -34,5 +34,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
NetworkNameTextBlock.Text = network.Name; NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%"; SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
} }
public void SetFocus()
{
Button.Focus();
}
} }
} }

View file

@ -45,10 +45,50 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
adapter.Changed += () => Dispatcher.InvokeAsync(Update); adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Button.PreviewKeyDown += (o, args) =>
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; {
Popup.Closed += (o, args) => Grid.Background = originalBrush; if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
Grid.Background = Brushes.Gray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
var btn = WirelessNetworksStackPanel.Children[0] as NetworkButton;
if (btn != null)
{
btn.SetFocus();
}
}
}));
};
Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
Update(); Update();
@ -121,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
{ {
Button.ToolTip = text; Button.ToolTip = text;
Text.Text = text; Text.Text = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text); Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
} }
private UIElement GetWirelessIcon(int signalStrength) private UIElement GetWirelessIcon(int signalStrength)

View file

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

View file

@ -55,15 +55,41 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") }; RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon); Icon.Content = IconResourceLoader.Load(LoweredIcon);
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp; NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp; NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered); NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered); Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered);
} }

View file

@ -44,6 +44,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
Button.MouseEnter += (o, args) => WindowPopup.IsOpen = WindowStackPanel.Children.Count > 0; Button.MouseEnter += (o, args) => WindowPopup.IsOpen = WindowStackPanel.Children.Count > 0;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = WindowPopup.IsMouseOver || ActiveBar.IsMouseOver)); Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = WindowPopup.IsMouseOver || ActiveBar.IsMouseOver));
Button.ToolTip = application.Tooltip; Button.ToolTip = application.Tooltip;
System.Windows.Automation.AutomationProperties.SetName(Button, application.Tooltip);
WindowPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(WindowPopup_PlacementCallback); WindowPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(WindowPopup_PlacementCallback);
WindowPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = IsMouseOver)); WindowPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = IsMouseOver));

View file

@ -22,7 +22,7 @@
<TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" /> <TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="40"> <StackPanel Orientation="Horizontal" Height="40">
<Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="40"> <Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="40">
<ContentControl x:Name="PopupIcon" /> <ContentControl x:Name="PopupIcon" Focusable="False" />
</Button> </Button>
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True" KeyDown="Volume_KeyDown" <Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True" KeyDown="Volume_KeyDown"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="250" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted" /> IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="250" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted" />

View file

@ -48,12 +48,34 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
audio.VolumeChanged += Audio_VolumeChanged; audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click; MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") }; MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_NoDevice.xaml") }; NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_NoDevice.xaml") };
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
@ -67,6 +89,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
if (audio.HasOutputDevice) if (audio.HasOutputDevice)
@ -132,19 +155,24 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
this.muted = muted; this.muted = muted;
Button.ToolTip = info; Button.ToolTip = info;
System.Windows.Automation.AutomationProperties.SetName(Button, info);
Volume.ValueChanged -= Volume_ValueChanged; Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100); Volume.Value = Math.Round(volume * 100);
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
if (muted) if (muted)
{ {
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip); var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
PopupIcon.Content = IconResourceLoader.Load(MutedIcon); PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon); ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
} }
else else
{ {
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip); var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
PopupIcon.Content = LoadIcon(volume); PopupIcon.Content = LoadIcon(volume);
ButtonIcon.Content = LoadIcon(volume); ButtonIcon.Content = LoadIcon(volume);
} }

View file

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

View file

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

View file

@ -10,6 +10,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Media; using System.Windows.Media;
@ -53,9 +54,31 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus(); ((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus();
}))); })));
}; };
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
@ -67,6 +90,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
} }
@ -120,6 +144,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
LayoutCultureCode.Text = layout.CultureCode; LayoutCultureCode.Text = layout.CultureCode;
Button.ToolTip = tooltip; Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
} }
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View file

@ -34,5 +34,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
NetworkNameTextBlock.Text = network.Name; NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%"; SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
} }
public void SetFocus()
{
Button.Focus();
}
} }
} }

View file

@ -45,22 +45,56 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
var originalBrush = Button.Background; var originalBrush = Button.Background;
adapter.Changed += () => Dispatcher.InvokeAsync(Update); adapter.Changed += () => Dispatcher.InvokeAsync(Update);
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
Background = Brushes.LightGray; Background = Brushes.LightGray;
Button.Background = Brushes.LightGray; Button.Background = Brushes.LightGray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
var btn = WirelessNetworksStackPanel.Children[0] as NetworkButton;
if (btn != null)
{
btn.SetFocus();
}
}
}));
}; };
Popup.Closed += (o, args) => Popup.Closed += (o, args) =>
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
Update(); Update();
@ -87,8 +121,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
if (network.Status == ConnectionStatus.Connected) if (network.Status == ConnectionStatus.Connected)
{ {
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name); UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, Button.ToolTip as string);
} }
WirelessNetworksStackPanel.Children.Add(button); WirelessNetworksStackPanel.Children.Add(button);
@ -98,7 +131,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{ {
case ConnectionType.Wired: case ConnectionType.Wired:
Button.IsEnabled = false; Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWiredConnected); UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible; WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed; WirelessIcon.Visibility = Visibility.Collapsed;
break; break;
@ -109,7 +142,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
break; break;
default: default:
Button.IsEnabled = false; Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkNotAvailable); UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible; WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed; WirelessIcon.Visibility = Visibility.Collapsed;
break; break;
@ -118,19 +151,20 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
switch (adapter.Status) switch (adapter.Status)
{ {
case ConnectionStatus.Connected: case ConnectionStatus.Connected:
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
NetworkStatusIcon.Rotation = 0; NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false; NetworkStatusIcon.Spin = false;
break; break;
case ConnectionStatus.Connecting: case ConnectionStatus.Connecting:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnecting); UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0; NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true; NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2; NetworkStatusIcon.SpinDuration = 2;
break; break;
default: default:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkDisconnected); UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false; NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
@ -138,6 +172,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
} }
} }
private void UpdateText(string text)
{
Button.ToolTip = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength) private UIElement GetWirelessIcon(int signalStrength)
{ {
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0")); var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));

View file

@ -41,6 +41,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{ {
IconButton.ToolTip = notification.Tooltip; IconButton.ToolTip = notification.Tooltip;
IconButton.Content = IconResourceLoader.Load(notification.IconResource); IconButton.Content = IconResourceLoader.Load(notification.IconResource);
System.Windows.Automation.AutomationProperties.SetName(this, notification.Tooltip);
} }
} }
} }

View file

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

View file

@ -55,13 +55,35 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") }; RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon); Icon.Content = IconResourceLoader.Load(LoweredIcon);
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp; NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp; NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered); NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
Background = Brushes.LightGray; Background = Brushes.LightGray;
@ -71,6 +93,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
NotificationButton.Background = originalBrush; NotificationButton.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
} }

View file

@ -80,7 +80,7 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="FindMenuText" HorizontalAlignment="Left" Margin="10,0" VerticalAlignment="Center" /> <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}" TabIndex="33"> <Button Grid.Column="1" x:Name="FindMenuButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" TabIndex="33" AutomationProperties.LabeledBy="{Binding ElementName=FindMenuText}">
<fa:ImageAwesome Icon="Search" /> <fa:ImageAwesome Icon="Search" />
</Button> </Button>
</Grid> </Grid>

View file

@ -244,8 +244,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
ZoomLevel.Text = $"{value}%"; ZoomLevel.Text = $"{value}%";
var zoomButtonHelpText = text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0")); var zoomButtonName = text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0"));
ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, zoomButtonHelpText); ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, zoomButtonName);
}); });
} }
@ -477,9 +477,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
HomeButton.Click += (o, args) => HomeNavigationRequested?.Invoke(); HomeButton.Click += (o, args) => HomeNavigationRequested?.Invoke();
Loaded += BrowserWindow_Loaded; Loaded += BrowserWindow_Loaded;
MenuButton.Click += MenuButton_Click; 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.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver)); MenuPopup.LostFocus += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsKeyboardFocusWithin));
KeyDown += BrowserWindow_KeyDown; KeyDown += BrowserWindow_KeyDown;
KeyUp += BrowserWindow_KeyUp; KeyUp += BrowserWindow_KeyUp;
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; }; LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
@ -512,7 +511,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
var javascript = @" var javascript = @"
if (typeof __SEB_focusElement === 'undefined') { if (typeof __SEB_focusElement === 'undefined') {
__SEB_focusElement = function (forward) { __SEB_focusElement = function (forward) {
if (!document.body) { return; } if (!document.body)
return;
var items = [].map var items = [].map
.call(document.body.querySelectorAll(['input', 'select', 'a[href]', 'textarea', 'button', '[tabindex]']), function(el, i) { return { el, i } }) .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; }) .filter(function(e) { return e.el.tabIndex >= 0 && !e.el.disabled && e.el.offsetParent; })
@ -640,13 +640,13 @@ if (typeof __SEB_focusElement === 'undefined') {
private void LoadText() private void LoadText()
{ {
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem); DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem)); DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem));
FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive); FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem); FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_FindMenuItem)); FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_FindMenuItem));
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem); ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus)); ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus));
ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus)); ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus));
ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton)); ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton));
BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton)); BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton));
ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton)); ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton));
@ -654,6 +654,10 @@ if (typeof __SEB_focusElement === 'undefined') {
HomeButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_HomeButton)); HomeButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_HomeButton));
MenuButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_MenuButton)); MenuButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_MenuButton));
UrlTextBox.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_UrlTextBox)); UrlTextBox.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_UrlTextBox));
FindTextBox.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_SearchTextBox));
FindPreviousButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_SearchPrevious));
FindNextButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_SearchNext));
FindbarCloseButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_CloseButton));
} }
} }
} }

View file

@ -35,7 +35,9 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
Icon.Content = IconResourceLoader.Load(window?.Icon ?? application.Icon); Icon.Content = IconResourceLoader.Load(window?.Icon ?? application.Icon);
Text.Text = window?.Title ?? application.Name; Text.Text = window?.Title ?? application.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty); Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = window?.Title ?? application.Tooltip; var tooltip = window?.Title ?? application.Tooltip;
Button.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(Button, tooltip);
if (window != null) if (window != null)
{ {

View file

@ -21,7 +21,7 @@
<TextBlock x:Name="AudioDeviceName" Foreground="White" Margin="5" TextAlignment="Center" /> <TextBlock x:Name="AudioDeviceName" Foreground="White" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="60" Margin="5,0"> <StackPanel Orientation="Horizontal" Height="60" Margin="5,0">
<Button x:Name="MuteButton" Background="Transparent" Foreground="White" Padding="5" Template="{StaticResource ActionCenterButton}" Width="60"> <Button x:Name="MuteButton" Background="Transparent" Foreground="White" Padding="5" Template="{StaticResource ActionCenterButton}" Width="60">
<ContentControl x:Name="PopupIcon" /> <ContentControl x:Name="PopupIcon" Focusable="False" />
</Button> </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"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted"> IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted">

View file

@ -49,13 +49,39 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
audio.VolumeChanged += Audio_VolumeChanged; audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click; MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") }; MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_NoDevice.xaml") }; NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_NoDevice.xaml") };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
if (audio.HasOutputDevice) if (audio.HasOutputDevice)
@ -113,6 +139,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
this.muted = muted; this.muted = muted;
Button.ToolTip = info; Button.ToolTip = info;
System.Windows.Automation.AutomationProperties.SetName(Button, info);
Text.Text = info; Text.Text = info;
Volume.ValueChanged -= Volume_ValueChanged; Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100); Volume.Value = Math.Round(volume * 100);
@ -120,14 +147,18 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
if (muted) if (muted)
{ {
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon); ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
PopupIcon.Content = IconResourceLoader.Load(MutedIcon); PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
} }
else else
{ {
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
ButtonIcon.Content = LoadIcon(volume); ButtonIcon.Content = LoadIcon(volume);
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
PopupIcon.Content = LoadIcon(volume); PopupIcon.Content = LoadIcon(volume);
} }
} }

View file

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

View file

@ -6,7 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
@ -49,10 +51,36 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
LayoutsStackPanel.Children[0].Focus(); LayoutsStackPanel.Children[0].Focus();
})); }));
}; };
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
} }
private void Keyboard_LayoutChanged(IKeyboardLayout layout) private void Keyboard_LayoutChanged(IKeyboardLayout layout)
@ -96,6 +124,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
Text.Text = layout.CultureName; Text.Text = layout.CultureName;
Button.ToolTip = tooltip; Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
} }
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View file

@ -34,5 +34,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
NetworkNameTextBlock.Text = network.Name; NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%"; SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
} }
public void SetFocus()
{
Button.Focus();
}
} }
} }

View file

@ -45,10 +45,50 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
adapter.Changed += () => Dispatcher.InvokeAsync(Update); adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Button.PreviewKeyDown += (o, args) =>
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; {
Popup.Closed += (o, args) => Grid.Background = originalBrush; if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
Grid.Background = Brushes.Gray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
var btn = WirelessNetworksStackPanel.Children[0] as NetworkButton;
if (btn != null)
{
btn.SetFocus();
}
}
}));
};
Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
Update(); Update();
@ -121,7 +161,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{ {
Button.ToolTip = text; Button.ToolTip = text;
Text.Text = text; Text.Text = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text); Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
} }
private UIElement GetWirelessIcon(int signalStrength) private UIElement GetWirelessIcon(int signalStrength)

View file

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

View file

@ -55,15 +55,41 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") }; RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon); Icon.Content = IconResourceLoader.Load(LoweredIcon);
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp; NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp; NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered); NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush; Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered); Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered);
} }

View file

@ -44,6 +44,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
Button.MouseEnter += (o, args) => WindowPopup.IsOpen = WindowStackPanel.Children.Count > 0; Button.MouseEnter += (o, args) => WindowPopup.IsOpen = WindowStackPanel.Children.Count > 0;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = WindowPopup.IsMouseOver || ActiveBar.IsMouseOver)); Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = WindowPopup.IsMouseOver || ActiveBar.IsMouseOver));
Button.ToolTip = application.Tooltip; Button.ToolTip = application.Tooltip;
System.Windows.Automation.AutomationProperties.SetName(Button, application.Tooltip);
WindowPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(WindowPopup_PlacementCallback); WindowPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(WindowPopup_PlacementCallback);
WindowPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = IsMouseOver)); WindowPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = IsMouseOver));

View file

@ -22,7 +22,7 @@
<TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" /> <TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="60" Margin="5,0"> <StackPanel Orientation="Horizontal" Height="60" Margin="5,0">
<Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="60"> <Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="60">
<ContentControl x:Name="PopupIcon" /> <ContentControl x:Name="PopupIcon" Focusable="False" />
</Button> </Button>
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True" KeyDown="Volume_KeyDown" <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"> IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted">

View file

@ -48,12 +48,34 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
audio.VolumeChanged += Audio_VolumeChanged; audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click; MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") }; MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_NoDevice.xaml") }; NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_NoDevice.xaml") };
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
@ -67,6 +89,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
if (audio.HasOutputDevice) if (audio.HasOutputDevice)
@ -132,19 +155,24 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
this.muted = muted; this.muted = muted;
Button.ToolTip = info; Button.ToolTip = info;
System.Windows.Automation.AutomationProperties.SetName(Button, info);
Volume.ValueChanged -= Volume_ValueChanged; Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100); Volume.Value = Math.Round(volume * 100);
Volume.ValueChanged += Volume_ValueChanged; Volume.ValueChanged += Volume_ValueChanged;
if (muted) if (muted)
{ {
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip); var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
PopupIcon.Content = IconResourceLoader.Load(MutedIcon); PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon); ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
} }
else else
{ {
MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip); var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
System.Windows.Automation.AutomationProperties.SetName(MuteButton, tooltip);
PopupIcon.Content = LoadIcon(volume); PopupIcon.Content = LoadIcon(volume);
ButtonIcon.Content = LoadIcon(volume); ButtonIcon.Content = LoadIcon(volume);
} }

View file

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

View file

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

View file

@ -10,6 +10,7 @@ using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Media; using System.Windows.Media;
@ -53,9 +54,31 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus(); ((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus();
}))); })));
}; };
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
@ -67,6 +90,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
} }
@ -120,6 +144,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
LayoutCultureCode.Text = layout.CultureCode; LayoutCultureCode.Text = layout.CultureCode;
Button.ToolTip = tooltip; Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
} }
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)

View file

@ -34,5 +34,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
NetworkNameTextBlock.Text = network.Name; NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%"; SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
} }
public void SetFocus()
{
Button.Focus();
}
} }
} }

View file

@ -46,21 +46,55 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
adapter.Changed += () => Dispatcher.InvokeAsync(Update); adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
Background = Brushes.LightGray; Background = Brushes.LightGray;
Button.Background = Brushes.LightGray; Button.Background = Brushes.LightGray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
var btn = WirelessNetworksStackPanel.Children[0] as NetworkButton;
if (btn != null)
{
btn.SetFocus();
}
}
}));
}; };
Popup.Closed += (o, args) => Popup.Closed += (o, args) =>
{ {
Background = originalBrush; Background = originalBrush;
Button.Background = originalBrush; Button.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
Update(); Update();
@ -87,8 +121,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
if (network.Status == ConnectionStatus.Connected) if (network.Status == ConnectionStatus.Connected)
{ {
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength); WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name); UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
Button.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, Button.ToolTip as string);
} }
WirelessNetworksStackPanel.Children.Add(button); WirelessNetworksStackPanel.Children.Add(button);
@ -98,7 +131,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{ {
case ConnectionType.Wired: case ConnectionType.Wired:
Button.IsEnabled = false; Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWiredConnected); UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible; WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed; WirelessIcon.Visibility = Visibility.Collapsed;
break; break;
@ -109,7 +142,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
break; break;
default: default:
Button.IsEnabled = false; Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkNotAvailable); UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible; WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed; WirelessIcon.Visibility = Visibility.Collapsed;
break; break;
@ -118,19 +151,20 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
switch (adapter.Status) switch (adapter.Status)
{ {
case ConnectionStatus.Connected: case ConnectionStatus.Connected:
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
NetworkStatusIcon.Rotation = 0; NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false; NetworkStatusIcon.Spin = false;
break; break;
case ConnectionStatus.Connecting: case ConnectionStatus.Connecting:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnecting); UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0; NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true; NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2; NetworkStatusIcon.SpinDuration = 2;
break; break;
default: default:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkDisconnected); UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange); NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false; NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0); WirelessIcon.Child = GetWirelessIcon(0);
@ -138,6 +172,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
} }
} }
private void UpdateText(string text)
{
Button.ToolTip = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength) private UIElement GetWirelessIcon(int signalStrength)
{ {
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0")); var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));

View file

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

View file

@ -55,13 +55,35 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") }; RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon); Icon.Content = IconResourceLoader.Load(LoweredIcon);
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp; NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp; NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered); NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback); Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Popup.Opened += (o, args) =>
{ {
Background = Brushes.LightGray; Background = Brushes.LightGray;
@ -72,6 +94,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{ {
Background = originalBrush; Background = originalBrush;
NotificationButton.Background = originalBrush; NotificationButton.Background = originalBrush;
lastOpenedBySpacePress = false;
}; };
} }

View file

@ -244,8 +244,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
Dispatcher.Invoke(() => Dispatcher.Invoke(() =>
{ {
ZoomLevel.Text = $"{value}%"; ZoomLevel.Text = $"{value}%";
var zoomButtonHelpText = text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0")); var zoomButtonName = text.Get(TextKey.BrowserWindow_ZoomLevelReset).Replace("%%ZOOM%%", value.ToString("0"));
ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, zoomButtonHelpText); ZoomResetButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, zoomButtonName);
}); });
} }
@ -473,9 +473,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
HomeButton.Click += (o, args) => HomeNavigationRequested?.Invoke(); HomeButton.Click += (o, args) => HomeNavigationRequested?.Invoke();
Loaded += BrowserWindow_Loaded; Loaded += BrowserWindow_Loaded;
MenuButton.Click += MenuButton_Click; 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.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver)); MenuPopup.LostFocus += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsKeyboardFocusWithin));
KeyDown += BrowserWindow_KeyDown; KeyDown += BrowserWindow_KeyDown;
KeyUp += BrowserWindow_KeyUp; KeyUp += BrowserWindow_KeyUp;
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; }; LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
@ -646,13 +645,13 @@ if (typeof __SEB_focusElement === 'undefined') {
private void LoadText() private void LoadText()
{ {
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem); DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem)); DeveloperConsoleButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem));
FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive); FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem); FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_FindMenuItem)); FindMenuButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_FindMenuItem));
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem); ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus)); ZoomInButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ZoomMenuPlus));
ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.HelpTextProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus)); ZoomOutButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ZoomMenuMinus));
ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton)); ReloadButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ReloadButton));
BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton)); BackwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_BackwardButton));
ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton)); ForwardButton.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text.Get(TextKey.BrowserWindow_ForwardButton));