mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
452 lines
18 KiB
452 lines
18 KiB
//********************************************************* |
|
// |
|
// Copyright (c) Microsoft. All rights reserved. |
|
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF |
|
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY |
|
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR |
|
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. |
|
// |
|
//********************************************************* |
|
|
|
#include "pch.h" |
|
#include "LayoutAwarePage.h" |
|
#include "SuspensionManager.h" |
|
|
|
using namespace SDKSample::Common; |
|
|
|
using namespace Platform; |
|
using namespace Platform::Collections; |
|
using namespace Windows::Foundation; |
|
using namespace Windows::Foundation::Collections; |
|
using namespace Windows::System; |
|
using namespace Windows::UI::Core; |
|
using namespace Windows::UI::ViewManagement; |
|
using namespace Windows::UI::Xaml; |
|
using namespace Windows::UI::Xaml::Controls; |
|
using namespace Windows::UI::Xaml::Interop; |
|
using namespace Windows::UI::Xaml::Navigation; |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="LayoutAwarePage"/> class. |
|
/// </summary> |
|
LayoutAwarePage::LayoutAwarePage() |
|
{ |
|
if (Windows::ApplicationModel::DesignMode::DesignModeEnabled) |
|
{ |
|
return; |
|
} |
|
|
|
// Create an empty default view model |
|
DefaultViewModel = ref new Map<String^, Object^>(std::less<String^>()); |
|
|
|
// When this page is part of the visual tree make two changes: |
|
// 1) Map application view state to visual state for the page |
|
// 2) Handle keyboard and mouse navigation requests |
|
Loaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnLoaded); |
|
|
|
// Undo the same changes when the page is no longer visible |
|
Unloaded += ref new RoutedEventHandler(this, &LayoutAwarePage::OnUnloaded); |
|
} |
|
|
|
static DependencyProperty^ _defaultViewModelProperty = |
|
DependencyProperty::Register("DefaultViewModel", |
|
TypeName(IObservableMap<String^, Object^>::typeid), TypeName(LayoutAwarePage::typeid), nullptr); |
|
|
|
/// <summary> |
|
/// Identifies the <see cref="DefaultViewModel"/> dependency property. |
|
/// </summary> |
|
DependencyProperty^ LayoutAwarePage::DefaultViewModelProperty::get() |
|
{ |
|
return _defaultViewModelProperty; |
|
} |
|
|
|
/// <summary> |
|
/// Gets an implementation of <see cref="IObservableMap<String, Object>"/> designed to be |
|
/// used as a trivial view model. |
|
/// </summary> |
|
IObservableMap<String^, Object^>^ LayoutAwarePage::DefaultViewModel::get() |
|
{ |
|
return safe_cast<IObservableMap<String^, Object^>^>(GetValue(DefaultViewModelProperty)); |
|
} |
|
|
|
/// <summary> |
|
/// Sets an implementation of <see cref="IObservableMap<String, Object>"/> designed to be |
|
/// used as a trivial view model. |
|
/// </summary> |
|
void LayoutAwarePage::DefaultViewModel::set(IObservableMap<String^, Object^>^ value) |
|
{ |
|
SetValue(DefaultViewModelProperty, value); |
|
} |
|
|
|
/// <summary> |
|
/// Invoked when the page is part of the visual tree |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="e">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) |
|
{ |
|
this->StartLayoutUpdates(sender, e); |
|
|
|
// Keyboard and mouse navigation only apply when occupying the entire window |
|
if (this->ActualHeight == Window::Current->Bounds.Height && |
|
this->ActualWidth == Window::Current->Bounds.Width) |
|
{ |
|
// Listen to the window directly so focus isn't required |
|
_acceleratorKeyEventToken = Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated += |
|
ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(this, |
|
&LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated); |
|
_pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed += |
|
ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, |
|
&LayoutAwarePage::CoreWindow_PointerPressed); |
|
_navigationShortcutsRegistered = true; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked when the page is removed from visual tree |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="e">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) |
|
{ |
|
if (_navigationShortcutsRegistered) |
|
{ |
|
Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated -= _acceleratorKeyEventToken; |
|
Window::Current->CoreWindow->PointerPressed -= _pointerPressedEventToken; |
|
_navigationShortcutsRegistered = false; |
|
} |
|
StopLayoutUpdates(sender, e); |
|
} |
|
|
|
#pragma region Navigation support |
|
|
|
/// <summary> |
|
/// Invoked as an event handler to navigate backward in the page's associated <see cref="Frame"/> |
|
/// until it reaches the top of the navigation stack. |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="e">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::GoHome(Object^ sender, RoutedEventArgs^ e) |
|
{ |
|
(void) sender; // Unused parameter |
|
(void) e; // Unused parameter |
|
|
|
// Use the navigation frame to return to the topmost page |
|
if (Frame != nullptr) |
|
{ |
|
while (Frame->CanGoBack) |
|
{ |
|
Frame->GoBack(); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked as an event handler to navigate backward in the navigation stack |
|
/// associated with this page's <see cref="Frame"/>. |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="e">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::GoBack(Object^ sender, RoutedEventArgs^ e) |
|
{ |
|
(void) sender; // Unused parameter |
|
(void) e; // Unused parameter |
|
|
|
// Use the navigation frame to return to the previous page |
|
if (Frame != nullptr && Frame->CanGoBack) |
|
{ |
|
Frame->GoBack(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked as an event handler to navigate forward in the navigation stack |
|
/// associated with this page's <see cref="Frame"/>. |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="e">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::GoForward(Object^ sender, RoutedEventArgs^ e) |
|
{ |
|
(void) sender; // Unused parameter |
|
(void) e; // Unused parameter |
|
|
|
// Use the navigation frame to advance to the next page |
|
if (Frame != nullptr && Frame->CanGoForward) |
|
{ |
|
Frame->GoForward(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked on every keystroke, including system keys such as Alt key combinations, when |
|
/// this page is active and occupies the entire window. Used to detect keyboard navigation |
|
/// between pages even when the page itself doesn't have focus. |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="args">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher^ sender, AcceleratorKeyEventArgs^ args) |
|
{ |
|
auto virtualKey = args->VirtualKey; |
|
|
|
// Only investigate further when Left, Right, or the dedicated Previous or Next keys |
|
// are pressed |
|
if ((args->EventType == CoreAcceleratorKeyEventType::SystemKeyDown || |
|
args->EventType == CoreAcceleratorKeyEventType::KeyDown) && |
|
(virtualKey == VirtualKey::Left || virtualKey == VirtualKey::Right || |
|
(int)virtualKey == 166 || (int)virtualKey == 167)) |
|
{ |
|
auto coreWindow = Window::Current->CoreWindow; |
|
auto downState = Windows::UI::Core::CoreVirtualKeyStates::Down; |
|
bool menuKey = (coreWindow->GetKeyState(VirtualKey::Menu) & downState) == downState; |
|
bool controlKey = (coreWindow->GetKeyState(VirtualKey::Control) & downState) == downState; |
|
bool shiftKey = (coreWindow->GetKeyState(VirtualKey::Shift) & downState) == downState; |
|
bool noModifiers = !menuKey && !controlKey && !shiftKey; |
|
bool onlyAlt = menuKey && !controlKey && !shiftKey; |
|
|
|
if (((int)virtualKey == 166 && noModifiers) || |
|
(virtualKey == VirtualKey::Left && onlyAlt)) |
|
{ |
|
// When the previous key or Alt+Left are pressed navigate back |
|
args->Handled = true; |
|
GoBack(this, ref new RoutedEventArgs()); |
|
} |
|
else if (((int)virtualKey == 167 && noModifiers) || |
|
(virtualKey == VirtualKey::Right && onlyAlt)) |
|
{ |
|
// When the next key or Alt+Right are pressed navigate forward |
|
args->Handled = true; |
|
GoForward(this, ref new RoutedEventArgs()); |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked on every mouse click, touch screen tap, or equivalent interaction when this |
|
/// page is active and occupies the entire window. Used to detect browser-style next and |
|
/// previous mouse button clicks to navigate between pages. |
|
/// </summary> |
|
/// <param name="sender">Instance that triggered the event.</param> |
|
/// <param name="args">Event data describing the conditions that led to the event.</param> |
|
void LayoutAwarePage::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEventArgs^ args) |
|
{ |
|
auto properties = args->CurrentPoint->Properties; |
|
|
|
// Ignore button chords with the left, right, and middle buttons |
|
if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed || |
|
properties->IsMiddleButtonPressed) return; |
|
|
|
// If back or foward are pressed (but not both) navigate appropriately |
|
bool backPressed = properties->IsXButton1Pressed; |
|
bool forwardPressed = properties->IsXButton2Pressed; |
|
if (backPressed ^ forwardPressed) |
|
{ |
|
args->Handled = true; |
|
if (backPressed) GoBack(this, ref new RoutedEventArgs()); |
|
if (forwardPressed) GoForward(this, ref new RoutedEventArgs()); |
|
} |
|
} |
|
|
|
#pragma endregion |
|
|
|
#pragma region Visual state switching |
|
|
|
/// <summary> |
|
/// Invoked as an event handler, typically on the <see cref="Loaded"/> event of a |
|
/// <see cref="Control"/> within the page, to indicate that the sender should start receiving |
|
/// visual state management changes that correspond to application view state changes. |
|
/// </summary> |
|
/// <param name="sender">Instance of <see cref="Control"/> that supports visual state management |
|
/// corresponding to view states.</param> |
|
/// <param name="e">Event data that describes how the request was made.</param> |
|
/// <remarks>The current view state will immediately be used to set the corresponding visual state |
|
/// when layout updates are requested. A corresponding <see cref="Unloaded"/> event handler |
|
/// connected to <see cref="StopLayoutUpdates"/> is strongly encouraged. Instances of |
|
/// <see cref="LayoutAwarePage"/> automatically invoke these handlers in their Loaded and Unloaded |
|
/// events.</remarks> |
|
/// <seealso cref="DetermineVisualState"/> |
|
/// <seealso cref="InvalidateVisualState"/> |
|
void LayoutAwarePage::StartLayoutUpdates(Object^ sender, RoutedEventArgs^ e) |
|
{ |
|
(void) e; // Unused parameter |
|
|
|
auto control = safe_cast<Control^>(sender); |
|
if (_layoutAwareControls == nullptr) |
|
{ |
|
// Start listening to view state changes when there are controls interested in updates |
|
_layoutAwareControls = ref new Vector<Control^>(); |
|
_windowSizeEventToken = Window::Current->SizeChanged += ref new WindowSizeChangedEventHandler(this, &LayoutAwarePage::WindowSizeChanged); |
|
|
|
// Page receives notifications for children. Protect the page until we stopped layout updates for all controls. |
|
_this = this; |
|
} |
|
_layoutAwareControls->Append(control); |
|
|
|
// Set the initial visual state of the control |
|
VisualStateManager::GoToState(control, DetermineVisualState(ApplicationView::Value), false); |
|
} |
|
|
|
void LayoutAwarePage::WindowSizeChanged(Object^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ e) |
|
{ |
|
(void) sender; // Unused parameter |
|
(void) e; // Unused parameter |
|
|
|
InvalidateVisualState(); |
|
} |
|
|
|
/// <summary> |
|
/// Invoked as an event handler, typically on the <see cref="Unloaded"/> event of a |
|
/// <see cref="Control"/>, to indicate that the sender should start receiving visual state |
|
/// management changes that correspond to application view state changes. |
|
/// </summary> |
|
/// <param name="sender">Instance of <see cref="Control"/> that supports visual state management |
|
/// corresponding to view states.</param> |
|
/// <param name="e">Event data that describes how the request was made.</param> |
|
/// <remarks>The current view state will immediately be used to set the corresponding visual state |
|
/// when layout updates are requested.</remarks> |
|
/// <seealso cref="StartLayoutUpdates"/> |
|
void LayoutAwarePage::StopLayoutUpdates(Object^ sender, RoutedEventArgs^ e) |
|
{ |
|
(void) e; // Unused parameter |
|
|
|
auto control = safe_cast<Control^>(sender); |
|
unsigned int index; |
|
if (_layoutAwareControls != nullptr && _layoutAwareControls->IndexOf(control, &index)) |
|
{ |
|
_layoutAwareControls->RemoveAt(index); |
|
if (_layoutAwareControls->Size == 0) |
|
{ |
|
// Stop listening to view state changes when no controls are interested in updates |
|
Window::Current->SizeChanged -= _windowSizeEventToken; |
|
_layoutAwareControls = nullptr; |
|
// Last control has received the Unload notification. |
|
_this = nullptr; |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Translates <see cref="ApplicationViewState"/> values into strings for visual state management |
|
/// within the page. The default implementation uses the names of enum values. Subclasses may |
|
/// override this method to control the mapping scheme used. |
|
/// </summary> |
|
/// <param name="viewState">View state for which a visual state is desired.</param> |
|
/// <returns>Visual state name used to drive the <see cref="VisualStateManager"/></returns> |
|
/// <seealso cref="InvalidateVisualState"/> |
|
String^ LayoutAwarePage::DetermineVisualState(ApplicationViewState viewState) |
|
{ |
|
switch (viewState) |
|
{ |
|
case ApplicationViewState::Filled: |
|
return "Filled"; |
|
case ApplicationViewState::Snapped: |
|
return "Snapped"; |
|
case ApplicationViewState::FullScreenPortrait: |
|
return "FullScreenPortrait"; |
|
case ApplicationViewState::FullScreenLandscape: |
|
default: |
|
return "FullScreenLandscape"; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Updates all controls that are listening for visual state changes with the correct visual |
|
/// state. |
|
/// </summary> |
|
/// <remarks> |
|
/// Typically used in conjunction with overriding <see cref="DetermineVisualState"/> to |
|
/// signal that a different value may be returned even though the view state has not changed. |
|
/// </remarks> |
|
void LayoutAwarePage::InvalidateVisualState() |
|
{ |
|
if (_layoutAwareControls != nullptr) |
|
{ |
|
String^ visualState = DetermineVisualState(ApplicationView::Value); |
|
auto controlIterator = _layoutAwareControls->First(); |
|
while (controlIterator->HasCurrent) |
|
{ |
|
auto control = controlIterator->Current; |
|
VisualStateManager::GoToState(control, visualState, false); |
|
controlIterator->MoveNext(); |
|
} |
|
} |
|
} |
|
|
|
#pragma endregion |
|
|
|
#pragma region Process lifetime management |
|
|
|
/// <summary> |
|
/// Invoked when this page is about to be displayed in a Frame. |
|
/// </summary> |
|
/// <param name="e">Event data that describes how this page was reached. The Parameter |
|
/// property provides the group to be displayed.</param> |
|
void LayoutAwarePage::OnNavigatedTo(NavigationEventArgs^ e) |
|
{ |
|
// Returning to a cached page through navigation shouldn't trigger state loading |
|
if (_pageKey != nullptr) return; |
|
|
|
auto frameState = SuspensionManager::SessionStateForFrame(Frame); |
|
_pageKey = "Page-" + Frame->BackStackDepth; |
|
|
|
if (e->NavigationMode == NavigationMode::New) |
|
{ |
|
// Clear existing state for forward navigation when adding a new page to the |
|
// navigation stack |
|
auto nextPageKey = _pageKey; |
|
int nextPageIndex = Frame->BackStackDepth; |
|
while (frameState->HasKey(nextPageKey)) |
|
{ |
|
frameState->Remove(nextPageKey); |
|
nextPageIndex++; |
|
nextPageKey = "Page-" + nextPageIndex; |
|
} |
|
|
|
// Pass the navigation parameter to the new page |
|
LoadState(e->Parameter, nullptr); |
|
} |
|
else |
|
{ |
|
// Pass the navigation parameter and preserved page state to the page, using |
|
// the same strategy for loading suspended state and recreating pages discarded |
|
// from cache |
|
LoadState(e->Parameter, safe_cast<IMap<String^, Object^>^>(frameState->Lookup(_pageKey))); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Invoked when this page will no longer be displayed in a Frame. |
|
/// </summary> |
|
/// <param name="e">Event data that describes how this page was reached. The Parameter |
|
/// property provides the group to be displayed.</param> |
|
void LayoutAwarePage::OnNavigatedFrom(NavigationEventArgs^ e) |
|
{ |
|
auto frameState = SuspensionManager::SessionStateForFrame(Frame); |
|
auto pageState = ref new Map<String^, Object^>(); |
|
SaveState(pageState); |
|
frameState->Insert(_pageKey, pageState); |
|
} |
|
|
|
/// <summary> |
|
/// Populates the page with content passed during navigation. Any saved state is also |
|
/// provided when recreating a page from a prior session. |
|
/// </summary> |
|
/// <param name="navigationParameter">The parameter value passed to |
|
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested. |
|
/// </param> |
|
/// <param name="pageState">A map of state preserved by this page during an earlier |
|
/// session. This will be null the first time a page is visited.</param> |
|
void LayoutAwarePage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState) |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Preserves state associated with this page in case the application is suspended or the |
|
/// page is discarded from the navigation cache. Values must conform to the serialization |
|
/// requirements of <see cref="SuspensionManager.SessionState"/>. |
|
/// </summary> |
|
/// <param name="pageState">An empty map to be populated with serializable state.</param> |
|
void LayoutAwarePage::SaveState(IMap<String^, Object^>^ pageState) |
|
{ |
|
} |
|
|
|
#pragma endregion
|
|
|