877 lines
33 KiB
C++
877 lines
33 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
#include "pch.h"
|
|
#include "KeyboardShortcutManager.h"
|
|
#include "AppResourceProvider.h"
|
|
#include "ApplicationViewModel.h"
|
|
#include "LocalizationSettings.h"
|
|
|
|
using namespace Concurrency;
|
|
using namespace Platform;
|
|
using namespace std;
|
|
using namespace Windows::ApplicationModel::Resources;
|
|
using namespace Windows::UI::Xaml;
|
|
using namespace Windows::UI::Xaml::Controls;
|
|
using namespace Windows::Foundation;
|
|
using namespace Windows::Foundation::Collections;
|
|
using namespace Windows::UI::Core;
|
|
using namespace Windows::UI::Xaml::Controls::Primitives;
|
|
using namespace Windows::System;
|
|
using namespace Utils;
|
|
using namespace CalculatorApp;
|
|
using namespace CalculatorApp::Common;
|
|
using namespace CalculatorApp::ViewModel;
|
|
|
|
namespace MUXC = Microsoft::UI::Xaml::Controls;
|
|
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, Character);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKey);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlChord);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyShiftChord);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyAltChord);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlShiftChord);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyInverseChord);
|
|
DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlInverseChord);
|
|
|
|
static multimap<int, multimap<wchar_t, WeakReference>> s_CharacterForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeysForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlChordsForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyShiftChordsForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyAltChordsForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlShiftChordsForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyInverseChordsForButtons;
|
|
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlInverseChordsForButtons;
|
|
|
|
static const TimeSpan c_lightUpTime = { 500000 }; // Quarter of a second
|
|
static multimap<int, bool> s_ShiftKeyPressed;
|
|
static multimap<int, bool> s_ControlKeyPressed;
|
|
static multimap<int, bool> s_ShiftButtonChecked;
|
|
static multimap<int, bool> s_IsDropDownOpen;
|
|
|
|
static reader_writer_lock s_keyboardShortcutMapLock;
|
|
|
|
namespace CalculatorApp
|
|
{
|
|
namespace Common
|
|
{
|
|
// Lights up all of the buttons in the given range
|
|
// The range is defined by a pair of iterators
|
|
template <typename T>
|
|
void LightUpButtons(const T& buttons)
|
|
{
|
|
auto iterator = buttons.first;
|
|
for (; iterator != buttons.second; ++iterator)
|
|
{
|
|
auto button = iterator->second.Resolve<ButtonBase>();
|
|
if (button && button->IsEnabled)
|
|
{
|
|
LightUpButton(button);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LightUpButton(ButtonBase^ button)
|
|
{
|
|
// If the button is a toggle button then we don't need
|
|
// to change the UI of the button
|
|
if (dynamic_cast<ToggleButton^>(button))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// The button will go into the visual Pressed state with this call
|
|
VisualStateManager::GoToState(button, "Pressed", true);
|
|
|
|
// This timer will fire after c_lightUpTime and make the button
|
|
// go back to the normal state.
|
|
// This timer will only fire once after which it will be destroyed
|
|
auto timer = ref new DispatcherTimer();
|
|
timer->Interval = c_lightUpTime;
|
|
|
|
WeakReference timerWeakReference(timer);
|
|
WeakReference buttonWeakReference(button);
|
|
timer->Tick += ref new EventHandler<Object^>(
|
|
[buttonWeakReference, timerWeakReference](Object^, Object^)
|
|
{
|
|
auto button = buttonWeakReference.Resolve<ButtonBase>();
|
|
if (button)
|
|
{
|
|
VisualStateManager::GoToState(button, "Normal", true);
|
|
}
|
|
|
|
// Cancel the timer after we're done so it only fires once
|
|
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
|
|
if (timer)
|
|
{
|
|
timer->Stop();
|
|
}
|
|
});
|
|
timer->Start();
|
|
}
|
|
|
|
// Looks for the first button reference that it can resolve
|
|
// and execute its command.
|
|
// NOTE: It is assumed that all buttons associated with a particular
|
|
// key have the same command
|
|
template <typename T>
|
|
void RunFirstEnabledButtonCommand(const T& buttons)
|
|
{
|
|
auto buttonIterator = buttons.first;
|
|
for (; buttonIterator != buttons.second; ++buttonIterator)
|
|
{
|
|
auto button = buttonIterator->second.Resolve<ButtonBase>();
|
|
if (button && button->IsEnabled)
|
|
{
|
|
RunButtonCommand(button);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RunButtonCommand(ButtonBase^ button)
|
|
{
|
|
if (button->IsEnabled)
|
|
{
|
|
auto command = button->Command;
|
|
auto parameter = button->CommandParameter;
|
|
if (command && command->CanExecute(parameter))
|
|
{
|
|
command->Execute(parameter);
|
|
}
|
|
|
|
auto radio = dynamic_cast<RadioButton^>(button);
|
|
if (radio)
|
|
{
|
|
radio->IsChecked = true;
|
|
return;
|
|
}
|
|
|
|
auto toggle = dynamic_cast<ToggleButton^>(button);
|
|
if (toggle)
|
|
{
|
|
toggle->IsChecked = !toggle->IsChecked->Value;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static multimap<int, bool> s_ignoreNextEscape;
|
|
static multimap<int, bool> s_keepIgnoringEscape;
|
|
static multimap<int, bool> s_fHonorShortcuts;
|
|
static multimap<int, bool> s_fHandledEnter;
|
|
static multimap<int, Flyout^> s_AboutFlyout;
|
|
|
|
void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
|
{
|
|
s_ignoreNextEscape.erase(viewId);
|
|
s_ignoreNextEscape.insert(std::make_pair(viewId, true));
|
|
}
|
|
|
|
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
|
{
|
|
s_keepIgnoringEscape.erase(viewId);
|
|
s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::HonorEscape()
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
|
{
|
|
s_ignoreNextEscape.erase(viewId);
|
|
s_ignoreNextEscape.insert(std::make_pair(viewId, false));
|
|
}
|
|
|
|
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
|
{
|
|
s_keepIgnoringEscape.erase(viewId);
|
|
s_keepIgnoringEscape.insert(std::make_pair(viewId, false));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnCharacterPropertyChanged(
|
|
DependencyObject^ target,
|
|
String^ oldValue,
|
|
String^ newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = safe_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_CharacterForButtons.find(viewId);
|
|
|
|
if (iterViewMap != s_CharacterForButtons.end())
|
|
{
|
|
if (oldValue)
|
|
{
|
|
iterViewMap->second.erase(oldValue->Data()[0]);
|
|
}
|
|
|
|
if (newValue)
|
|
{
|
|
if (newValue == L".")
|
|
{
|
|
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
|
iterViewMap->second.insert(std::make_pair(decSep, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s_CharacterForButtons.insert(std::make_pair(viewId, std::multimap<wchar_t, WeakReference>()));
|
|
|
|
if (newValue == L".")
|
|
{
|
|
wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator();
|
|
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(decSep, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = static_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeysForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeysForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeysForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeysForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyControlChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
Control^ control = dynamic_cast<ButtonBase^>(target);
|
|
|
|
if (control == nullptr)
|
|
{
|
|
// Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case
|
|
control = safe_cast<MUXC::NavigationView^>(target);
|
|
}
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyControlChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyControlChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(control)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(control)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyShiftChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = safe_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyShiftChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyShiftChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyAltChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
MUXC::NavigationView^ navView = safe_cast<MUXC::NavigationView^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyAltChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyAltChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(navView)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyControlShiftChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = safe_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyControlShiftChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyControlShiftChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyInverseChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = safe_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnVirtualKeyControlInverseChordPropertyChanged(
|
|
DependencyObject^ target,
|
|
MyVirtualKey /*oldValue*/,
|
|
MyVirtualKey newValue)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto button = safe_cast<ButtonBase^>(target);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
|
|
|
// Check if the View Id has already been registered
|
|
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
|
{
|
|
iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
else
|
|
{
|
|
// If the View Id is not already registered, then register it and make the entry
|
|
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button)));
|
|
}
|
|
}
|
|
|
|
// In the three event handlers bellow we will not mark the event as handled
|
|
// because this is a sumplemental operation and we don't want to interfere with
|
|
// the normal keyboard handling.
|
|
void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, CharacterReceivedEventArgs^ args)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
|
|
|
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
|
{
|
|
if (currentHonorShortcuts->second)
|
|
{
|
|
wchar_t character = static_cast<wchar_t>(args->KeyCode);
|
|
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
|
|
|
|
RunFirstEnabledButtonCommand(buttons);
|
|
|
|
LightUpButtons(buttons);
|
|
}
|
|
}
|
|
}
|
|
|
|
const std::multimap<MyVirtualKey, WeakReference>& GetCurrentKeyDictionary(MyVirtualKey key, bool altPressed = false)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (altPressed)
|
|
{
|
|
return s_VirtualKeyAltChordsForButtons.find(viewId)->second;
|
|
}
|
|
else if ((s_ShiftKeyPressed.find(viewId)->second) && ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down))
|
|
{
|
|
return s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second;
|
|
}
|
|
else if (s_ShiftKeyPressed.find(viewId)->second)
|
|
{
|
|
return s_VirtualKeyShiftChordsForButtons.find(viewId)->second;
|
|
}
|
|
else if (s_ShiftButtonChecked.find(viewId)->second)
|
|
{
|
|
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
|
{
|
|
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
|
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
|
{
|
|
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
|
{
|
|
if (key == iterator->first)
|
|
{
|
|
return s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
|
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
|
{
|
|
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
|
{
|
|
if (key == iterator->first)
|
|
{
|
|
return s_VirtualKeyInverseChordsForButtons.find(viewId)->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)
|
|
{
|
|
return s_VirtualKeyControlChordsForButtons.find(viewId)->second;
|
|
}
|
|
else
|
|
{
|
|
return s_VirtualKeysForButtons.find(viewId)->second;
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
|
{
|
|
// If keyboard shortcuts like Ctrl+C or Ctrl+V are not handled
|
|
if (!args->Handled)
|
|
{
|
|
auto key = args->VirtualKey;
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
|
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
|
|
|
bool isControlKeyPressed = (currentControlKeyPressed != s_ControlKeyPressed.end()) && (currentControlKeyPressed->second);
|
|
bool isShiftKeyPressed = (currentShiftKeyPressed != s_ShiftKeyPressed.end()) && (currentShiftKeyPressed->second);
|
|
|
|
// Handle Ctrl + E for DateCalculator
|
|
if ((key == VirtualKey::E) &&
|
|
isControlKeyPressed &&
|
|
!isShiftKeyPressed)
|
|
{
|
|
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
|
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
|
auto navView = buttons.first->second.Resolve<MUXC::NavigationView>();
|
|
auto appViewModel = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
|
appViewModel->Mode = ViewMode::Date;
|
|
auto categoryName = AppResourceProvider::GetInstance().GetResourceString(L"DateCalculationModeText");
|
|
appViewModel->CategoryName = categoryName;
|
|
|
|
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
|
auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date);
|
|
navView->SelectedItem = menuItems->GetAt(flatIndex);
|
|
return;
|
|
}
|
|
|
|
auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId);
|
|
|
|
auto currentIgnoreNextEscape = s_ignoreNextEscape.find(viewId);
|
|
|
|
if (currentIgnoreNextEscape != s_ignoreNextEscape.end())
|
|
{
|
|
if (currentIgnoreNextEscape->second && key == VirtualKey::Escape)
|
|
{
|
|
auto currentKeepIgnoringEscape = s_keepIgnoringEscape.find(viewId);
|
|
|
|
if (currentKeepIgnoringEscape != s_keepIgnoringEscape.end())
|
|
{
|
|
if (!currentKeepIgnoringEscape->second)
|
|
{
|
|
HonorEscape();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (key == VirtualKey::Control)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
|
|
|
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
|
{
|
|
s_ControlKeyPressed.erase(viewId);
|
|
s_ControlKeyPressed.insert(std::make_pair(viewId, true));
|
|
}
|
|
return;
|
|
}
|
|
else if (key == VirtualKey::Shift)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
|
|
|
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
|
{
|
|
s_ShiftKeyPressed.erase(viewId);
|
|
s_ShiftKeyPressed.insert(std::make_pair(viewId, true));
|
|
}
|
|
return;
|
|
}
|
|
|
|
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
|
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
|
|
|
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
|
|
|
|
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
|
{
|
|
if (currentHonorShortcuts->second)
|
|
{
|
|
RunFirstEnabledButtonCommand(buttons);
|
|
|
|
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed.
|
|
// When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open.
|
|
// Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
|
|
if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second)
|
|
{
|
|
// Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed
|
|
if (!(isControlKeyPressed && (key == VirtualKey::C || key == VirtualKey::V || key == VirtualKey::Insert))
|
|
&& !(isShiftKeyPressed && (key == VirtualKey::Insert)))
|
|
{
|
|
LightUpButtons(buttons);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow^ sender, KeyEventArgs^ args)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
auto key = args->VirtualKey;
|
|
|
|
if (args->VirtualKey == VirtualKey::Shift)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId);
|
|
|
|
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
|
{
|
|
s_ShiftKeyPressed.erase(viewId);
|
|
s_ShiftKeyPressed.insert(std::make_pair(viewId, false));
|
|
}
|
|
}
|
|
else if (args->VirtualKey == VirtualKey::Control)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId);
|
|
|
|
if (currentControlKeyPressed != s_ControlKeyPressed.end())
|
|
{
|
|
s_ControlKeyPressed.erase(viewId);
|
|
s_ControlKeyPressed.insert(std::make_pair(viewId, false));
|
|
}
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher^, AcceleratorKeyEventArgs^ args)
|
|
{
|
|
if (args->KeyStatus.IsKeyReleased)
|
|
{
|
|
auto key = args->VirtualKey;
|
|
bool altPressed = args->KeyStatus.IsMenuKeyDown;
|
|
|
|
// If the Alt/Menu key is not pressed then we don't care about the key anymore
|
|
if (!altPressed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key), altPressed);
|
|
auto listItems = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
|
for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator)
|
|
{
|
|
auto item = listIterator->second.Resolve<MUXC::NavigationView>();
|
|
if (item != nullptr)
|
|
{
|
|
auto navView = safe_cast<MUXC::NavigationView^> (item);
|
|
|
|
auto menuItems = static_cast<IObservableVector<Object^>^>(navView->MenuItemsSource);
|
|
if (menuItems != nullptr)
|
|
{
|
|
auto vm = safe_cast<ApplicationViewModel^>(navView->DataContext);
|
|
if (nullptr != vm)
|
|
{
|
|
ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast<MyVirtualKey>(key));
|
|
if (NavCategory::IsValidViewMode(toMode))
|
|
{
|
|
vm->Mode = toMode;
|
|
navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (args->VirtualKey == VirtualKey::Escape)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
auto iterViewMap = s_AboutFlyout.find(viewId);
|
|
|
|
if ((iterViewMap != s_AboutFlyout.end()) && (iterViewMap->second != nullptr))
|
|
{
|
|
iterViewMap->second->Hide();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::Initialize()
|
|
{
|
|
auto coreWindow = Window::Current->CoreWindow;
|
|
coreWindow->CharacterReceived +=
|
|
ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(&KeyboardShortcutManager::OnCharacterReceivedHandler);
|
|
coreWindow->KeyDown +=
|
|
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyDownHandler);
|
|
coreWindow->KeyUp +=
|
|
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(&KeyboardShortcutManager::OnKeyUpHandler);
|
|
coreWindow->Dispatcher->AcceleratorKeyActivated +=
|
|
ref new TypedEventHandler<CoreDispatcher^, AcceleratorKeyEventArgs^>(&KeyboardShortcutManager::OnAcceleratorKeyActivated);
|
|
|
|
KeyboardShortcutManager::RegisterNewAppViewId();
|
|
}
|
|
|
|
void KeyboardShortcutManager::ShiftButtonChecked(bool checked)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end())
|
|
{
|
|
s_ShiftButtonChecked.erase(viewId);
|
|
s_ShiftButtonChecked.insert(std::make_pair(viewId, checked));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::UpdateDropDownState(bool isOpen)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end())
|
|
{
|
|
s_IsDropDownOpen.erase(viewId);
|
|
s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::UpdateDropDownState(Flyout^ aboutPageFlyout)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end())
|
|
{
|
|
s_AboutFlyout.erase(viewId);
|
|
s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::HonorShortcuts(bool allow)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end())
|
|
{
|
|
s_fHonorShortcuts.erase(viewId);
|
|
s_fHonorShortcuts.insert(std::make_pair(viewId, allow));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::HandledEnter(bool ishandled)
|
|
{
|
|
int viewId = Utils::GetWindowId();
|
|
|
|
if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end())
|
|
{
|
|
s_fHandledEnter.erase(viewId);
|
|
s_fHandledEnter.insert(std::make_pair(viewId, ishandled));
|
|
}
|
|
}
|
|
|
|
void KeyboardShortcutManager::RegisterNewAppViewId()
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
int appViewId = Utils::GetWindowId();
|
|
|
|
// Check if the View Id has already been registered
|
|
if (s_CharacterForButtons.find(appViewId) == s_CharacterForButtons.end())
|
|
{
|
|
s_CharacterForButtons.insert(std::make_pair(appViewId, std::multimap<wchar_t, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeysForButtons.find(appViewId) == s_VirtualKeysForButtons.end())
|
|
{
|
|
s_VirtualKeysForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyControlChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyAltChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyInverseChordsForButtons.find(appViewId) == s_VirtualKeyInverseChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
if (s_VirtualKeyControlInverseChordsForButtons.find(appViewId) == s_VirtualKeyControlInverseChordsForButtons.end())
|
|
{
|
|
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
|
}
|
|
|
|
s_ShiftKeyPressed.insert(std::make_pair(appViewId, false));
|
|
s_ControlKeyPressed.insert(std::make_pair(appViewId, false));
|
|
s_ShiftButtonChecked.insert(std::make_pair(appViewId, false));
|
|
s_IsDropDownOpen.insert(std::make_pair(appViewId, false));
|
|
s_ignoreNextEscape.insert(std::make_pair(appViewId, false));
|
|
s_keepIgnoringEscape.insert(std::make_pair(appViewId, false));
|
|
s_fHonorShortcuts.insert(std::make_pair(appViewId, true));
|
|
s_fHandledEnter.insert(std::make_pair(appViewId, true));
|
|
s_AboutFlyout.insert(std::make_pair(appViewId, nullptr));
|
|
}
|
|
|
|
void KeyboardShortcutManager::OnWindowClosed(int viewId)
|
|
{
|
|
// Writer lock for the static maps
|
|
reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock);
|
|
|
|
s_CharacterForButtons.erase(viewId);
|
|
|
|
s_VirtualKeysForButtons.erase(viewId);
|
|
s_VirtualKeyControlChordsForButtons.erase(viewId);
|
|
s_VirtualKeyShiftChordsForButtons.erase(viewId);
|
|
s_VirtualKeyAltChordsForButtons.erase(viewId);
|
|
s_VirtualKeyControlShiftChordsForButtons.erase(viewId);
|
|
s_VirtualKeyInverseChordsForButtons.erase(viewId);
|
|
s_VirtualKeyControlInverseChordsForButtons.erase(viewId);
|
|
|
|
s_ShiftKeyPressed.erase(viewId);
|
|
s_ControlKeyPressed.erase(viewId);
|
|
s_ShiftButtonChecked.erase(viewId);
|
|
s_IsDropDownOpen.erase(viewId);
|
|
s_ignoreNextEscape.erase(viewId);
|
|
s_keepIgnoringEscape.erase(viewId);
|
|
s_fHonorShortcuts.erase(viewId);
|
|
s_fHandledEnter.erase(viewId);
|
|
s_AboutFlyout.erase(viewId);
|
|
}
|