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); } }