diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
index 747c081b..a4657b53 100644
--- a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
+++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs
@@ -137,6 +137,7 @@ namespace SafeExamBrowser.Browser
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
+ keyboardHandler.FindRequested += KeyboardHandler_FindRequested;
keyboardHandler.ReloadRequested += ReloadRequested;
keyboardHandler.ZoomInRequested += ZoomInRequested;
keyboardHandler.ZoomOutRequested += ZoomOutRequested;
@@ -201,6 +202,7 @@ namespace SafeExamBrowser.Browser
window.AddressChanged += Window_AddressChanged;
window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
window.DeveloperConsoleRequested += Window_DeveloperConsoleRequested;
+ window.FindRequested += Window_FindRequested;
window.ForwardNavigationRequested += Window_ForwardNavigationRequested;
window.ReloadRequested += ReloadRequested;
window.ZoomInRequested += ZoomInRequested;
@@ -353,6 +355,11 @@ namespace SafeExamBrowser.Browser
window.UpdateDownloadState(state);
}
+ private void KeyboardHandler_FindRequested()
+ {
+ window.ShowFindbar();
+ }
+
private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args)
{
var validCurrentUri = Uri.TryCreate(control.Address, UriKind.Absolute, out var currentUri);
@@ -492,6 +499,11 @@ namespace SafeExamBrowser.Browser
control.ShowDeveloperConsole();
}
+ private void Window_FindRequested(string term, bool isInitial, bool caseSensitive, bool forward = true)
+ {
+ control.Find(term, isInitial, caseSensitive, forward);
+ }
+
private void Window_ForwardNavigationRequested()
{
logger.Debug("Navigating forwards...");
diff --git a/SafeExamBrowser.Browser/BrowserControl.cs b/SafeExamBrowser.Browser/BrowserControl.cs
index b547a291..88ec7b22 100644
--- a/SafeExamBrowser.Browser/BrowserControl.cs
+++ b/SafeExamBrowser.Browser/BrowserControl.cs
@@ -98,6 +98,11 @@ namespace SafeExamBrowser.Browser
RequestHandler = requestHandler;
}
+ public void Find(string term, bool isInitial, bool caseSensitive, bool forward = true)
+ {
+ this.Find(0, term, forward, caseSensitive, !isInitial);
+ }
+
public void NavigateBackwards()
{
GetBrowser().GoBack();
diff --git a/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs b/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs
index 0cc71af5..9b09e95b 100644
--- a/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs
+++ b/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs
@@ -14,16 +14,22 @@ namespace SafeExamBrowser.Browser.Handlers
{
internal class KeyboardHandler : IKeyboardHandler
{
- public event ActionRequestedEventHandler ReloadRequested;
- public event ActionRequestedEventHandler ZoomInRequested;
- public event ActionRequestedEventHandler ZoomOutRequested;
- public event ActionRequestedEventHandler ZoomResetRequested;
+ internal event ActionRequestedEventHandler FindRequested;
+ internal event ActionRequestedEventHandler ReloadRequested;
+ internal event ActionRequestedEventHandler ZoomInRequested;
+ internal event ActionRequestedEventHandler ZoomOutRequested;
+ internal event ActionRequestedEventHandler ZoomResetRequested;
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int keyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
{
var ctrl = modifiers.HasFlag(CefEventFlags.ControlDown);
var shift = modifiers.HasFlag(CefEventFlags.ShiftDown);
+ if (type == KeyType.KeyUp && ctrl && (keyCode == (int) Keys.F))
+ {
+ FindRequested?.Invoke();
+ }
+
if (type == KeyType.KeyUp && ((keyCode == (int) Keys.Add && ctrl) || (keyCode == (int) Keys.D1 && ctrl && shift)))
{
ZoomInRequested?.Invoke();
diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs
index 83ca185c..15b88a06 100644
--- a/SafeExamBrowser.I18n.Contracts/TextKey.cs
+++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs
@@ -28,6 +28,8 @@ namespace SafeExamBrowser.I18n.Contracts
BrowserWindow_Downloading,
BrowserWindow_DownloadCancelled,
BrowserWindow_DownloadComplete,
+ BrowserWindow_FindCaseSensitive,
+ BrowserWindow_FindMenuItem,
BrowserWindow_ZoomMenuItem,
Build,
ExamSelectionDialog_Cancel,
diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml
index 08eceba8..38fb0225 100644
--- a/SafeExamBrowser.I18n/Data/de.xml
+++ b/SafeExamBrowser.I18n/Data/de.xml
@@ -42,6 +42,12 @@
Heruntergeladen.
+
+ Gross-/Kleinschreibung beachten
+
+
+ Seite durchsuchen...
+
Seiten-Zoom
diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml
index c09d532f..27fb436d 100644
--- a/SafeExamBrowser.I18n/Data/en.xml
+++ b/SafeExamBrowser.I18n/Data/en.xml
@@ -42,6 +42,12 @@
Downloaded.
+
+ Case-sensitive search
+
+
+ Search page...
+
Page Zoom
diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/Events/FindRequestedEventHandler.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/Events/FindRequestedEventHandler.cs
new file mode 100644
index 00000000..6eaf8464
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Contracts/Browser/Events/FindRequestedEventHandler.cs
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+namespace SafeExamBrowser.UserInterface.Contracts.Browser.Events
+{
+ ///
+ /// Indicates that a page search is being requested.
+ ///
+ public delegate void FindRequestedEventHandler(string term, bool isInitial, bool caseSensitive, bool forward = true);
+}
diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs
index db38bdc3..ae29b89d 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserControl.cs
@@ -56,6 +56,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
///
void Destroy();
+ ///
+ /// Attempts to find the given term on the current page according to the specified parameters.
+ ///
+ void Find(string term, bool isInitial, bool caseSensitive, bool forward = true);
+
///
/// Initializes the browser control.
///
diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
index 478b2cdf..767067a9 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
@@ -49,6 +49,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
///
event ActionRequestedEventHandler DeveloperConsoleRequested;
+ ///
+ /// Event fired when the user would like to search the current page.
+ ///
+ event FindRequestedEventHandler FindRequested;
+
///
/// Event fired when the user would like to navigate forwards.
///
@@ -74,6 +79,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
///
event ActionRequestedEventHandler ZoomResetRequested;
+ ///
+ /// Displays the find toolbar to search the content of a page.
+ ///
+ void ShowFindbar();
+
///
/// Updates the address bar of the browser window to the given value.
///
diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
index 737c7268..d790bdc3 100644
--- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
+++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
@@ -56,6 +56,7 @@
+
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml
index 19bc3228..9bfeb934 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml
@@ -19,6 +19,7 @@
+
@@ -71,6 +72,16 @@
+
+
+
+
+
+
+
+
@@ -89,5 +100,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs
index 0a1a5f13..c55bb55b 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/BrowserWindow.xaml.cs
@@ -47,6 +47,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
public event AddressChangedEventHandler AddressChanged;
public event ActionRequestedEventHandler BackwardNavigationRequested;
public event ActionRequestedEventHandler DeveloperConsoleRequested;
+ public event FindRequestedEventHandler FindRequested;
public event ActionRequestedEventHandler ForwardNavigationRequested;
public event ActionRequestedEventHandler ReloadRequested;
public event ActionRequestedEventHandler ZoomInRequested;
@@ -102,6 +103,15 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
Dispatcher.Invoke(base.Show);
}
+ public void ShowFindbar()
+ {
+ Dispatcher.InvokeAsync(() =>
+ {
+ Findbar.Visibility = Visibility.Visible;
+ FindTextBox.Focus();
+ });
+ }
+
public void UpdateAddress(string url)
{
Dispatcher.Invoke(() => UrlTextBox.Text = url);
@@ -186,6 +196,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
ReloadRequested?.Invoke();
}
+
+ if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && e.Key == Key.F)
+ {
+ ShowFindbar();
+ }
}
private void BrowserWindow_Loaded(object sender, RoutedEventArgs e)
@@ -198,6 +213,26 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
}
}
+ private void FindbarCloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ Findbar.Visibility = Visibility.Collapsed;
+ }
+
+ private void FindNextButton_Click(object sender, RoutedEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, false, FindCaseSensitiveCheckBox.IsChecked == true);
+ }
+
+ private void FindPreviousButton_Click(object sender, RoutedEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, false, FindCaseSensitiveCheckBox.IsChecked == true, false);
+ }
+
+ private void FindTextBox_KeyUp(object sender, KeyEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, true, FindCaseSensitiveCheckBox.IsChecked == true);
+ }
+
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
@@ -254,6 +289,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
+ FindbarCloseButton.Click += FindbarCloseButton_Click;
+ FindNextButton.Click += FindNextButton_Click;
+ FindPreviousButton.Click += FindPreviousButton_Click;
+ FindMenuButton.Click += (o, args) => ShowFindbar();
+ FindTextBox.KeyUp += FindTextBox_KeyUp;
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
Loaded += BrowserWindow_Loaded;
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
@@ -378,6 +418,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
private void LoadText()
{
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
+ FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
+ FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
}
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml
index 5b84eb9b..2cd8e7b6 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml
+++ b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml
@@ -19,6 +19,7 @@
+
@@ -71,6 +72,16 @@
+
+
+
+
+
+
+
+
@@ -89,5 +100,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs
index 954451e9..9395c245 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Windows/BrowserWindow.xaml.cs
@@ -47,6 +47,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
public event AddressChangedEventHandler AddressChanged;
public event ActionRequestedEventHandler BackwardNavigationRequested;
public event ActionRequestedEventHandler DeveloperConsoleRequested;
+ public event FindRequestedEventHandler FindRequested;
public event ActionRequestedEventHandler ForwardNavigationRequested;
public event ActionRequestedEventHandler ReloadRequested;
public event ActionRequestedEventHandler ZoomInRequested;
@@ -102,6 +103,15 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
Dispatcher.Invoke(base.Show);
}
+ public void ShowFindbar()
+ {
+ Dispatcher.InvokeAsync(() =>
+ {
+ Findbar.Visibility = Visibility.Visible;
+ FindTextBox.Focus();
+ });
+ }
+
public void UpdateAddress(string url)
{
Dispatcher.Invoke(() => UrlTextBox.Text = url);
@@ -186,6 +196,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
ReloadRequested?.Invoke();
}
+
+ if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && e.Key == Key.F)
+ {
+ ShowFindbar();
+ }
}
private void BrowserWindow_Loaded(object sender, RoutedEventArgs e)
@@ -198,6 +213,26 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
}
}
+ private void FindbarCloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ Findbar.Visibility = Visibility.Collapsed;
+ }
+
+ private void FindNextButton_Click(object sender, RoutedEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, false, FindCaseSensitiveCheckBox.IsChecked == true);
+ }
+
+ private void FindPreviousButton_Click(object sender, RoutedEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, false, FindCaseSensitiveCheckBox.IsChecked == true, false);
+ }
+
+ private void FindTextBox_KeyUp(object sender, KeyEventArgs e)
+ {
+ FindRequested?.Invoke(FindTextBox.Text, true, FindCaseSensitiveCheckBox.IsChecked == true);
+ }
+
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
@@ -254,6 +289,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
+ FindbarCloseButton.Click += FindbarCloseButton_Click;
+ FindNextButton.Click += FindNextButton_Click;
+ FindPreviousButton.Click += FindPreviousButton_Click;
+ FindMenuButton.Click += (o, args) => ShowFindbar();
+ FindTextBox.KeyUp += FindTextBox_KeyUp;
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
Loaded += BrowserWindow_Loaded;
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
@@ -388,6 +428,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
private void LoadText()
{
DeveloperConsoleText.Text = text.Get(TextKey.BrowserWindow_DeveloperConsoleMenuItem);
+ FindCaseSensitiveCheckBox.Content = text.Get(TextKey.BrowserWindow_FindCaseSensitive);
+ FindMenuText.Text = text.Get(TextKey.BrowserWindow_FindMenuItem);
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
}
}