diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index 5235932..a5de228 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -75,7 +75,7 @@ void ApplicationViewModel::Categories::set(IObservableVector void ApplicationViewModel::Initialize(ViewMode mode) { - if (!NavCategory::IsValidViewMode(mode) || !NavCategory::IsViewModeEnabled(mode)) + if (!NavCategoryStates::IsValidViewMode(mode) || !NavCategoryStates::IsViewModeEnabled(mode)) { mode = ViewMode::Standard; } @@ -124,7 +124,7 @@ bool ApplicationViewModel::TryRecoverFromNavigationModeFailure() void ApplicationViewModel::OnModeChanged() { - assert(NavCategory::IsValidViewMode(m_mode)); + assert(NavCategoryStates::IsValidViewMode(m_mode)); if (NavCategory::IsCalculatorViewMode(m_mode)) { if (!m_CalculatorViewModel) @@ -160,15 +160,15 @@ void ApplicationViewModel::OnModeChanged() } auto resProvider = AppResourceProvider::GetInstance(); - CategoryName = resProvider->GetResourceString(NavCategory::GetNameResourceKey(m_mode)); + CategoryName = resProvider->GetResourceString(NavCategoryStates::GetNameResourceKey(m_mode)); // Cast mode to an int in order to save it to app data. // Save the changed mode, so that the new window launches in this mode. // Don't save until after we have adjusted to the new mode, so we don't save a mode that fails to load. - ApplicationData::Current->LocalSettings->Values->Insert(ModePropertyName, NavCategory::Serialize(m_mode)); + ApplicationData::Current->LocalSettings->Values->Insert(ModePropertyName, NavCategoryStates::Serialize(m_mode)); // Log ModeChange event when not first launch, log WindowCreated on first launch - if (NavCategory::IsValidViewMode(m_PreviousMode)) + if (NavCategoryStates::IsValidViewMode(m_PreviousMode)) { TraceLogger::GetInstance()->LogModeChange(m_mode); } @@ -213,7 +213,7 @@ void ApplicationViewModel::SetMenuCategories() // Use the Categories property instead of the backing variable // because we want to take advantage of binding updates and // property setter logic. - Categories = NavCategoryGroup::CreateMenuOptions(); + Categories = NavCategoryStates::CreateMenuOptions(); } void ApplicationViewModel::ToggleAlwaysOnTop(float width, float height) diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index 44ae1ac..1e09072 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -97,7 +97,7 @@ bool CopyPasteManager::HasStringToPaste() String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, NumberBase programmerNumberBase, BitLength bitLengthType) { - return ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); + return ValidatePasteExpression(pastedText, mode, NavCategoryStates::GetGroupType(mode), programmerNumberBase, bitLengthType); } // return "NoOp" if pastedText is invalid else return pastedText diff --git a/src/CalcViewModel/Common/NavCategory.cpp b/src/CalcViewModel/Common/NavCategory.cpp index 32bc387..51cb8dc 100644 --- a/src/CalcViewModel/Common/NavCategory.cpp +++ b/src/CalcViewModel/Common/NavCategory.cpp @@ -13,7 +13,6 @@ using namespace CalculatorApp::ViewModel; using namespace Concurrency; using namespace Platform; using namespace Platform::Collections; -using namespace std; using namespace Windows::Foundation::Collections; using namespace Windows::Management::Policies; using namespace Windows::System; @@ -48,452 +47,230 @@ static constexpr int CURRENCY_ID = 16; static constexpr int GRAPHING_ID = 17; // ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^ -wchar_t* towchar_t(int number) +namespace // put the utils within this TU { - auto wstr = to_wstring(number); - return _wcsdup(wstr.c_str()); -} + Platform::Agile CurrentUser; + std::mutex GraphingModeCheckMutex; -bool IsGraphingModeAvailable() -{ - static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"); - return supportGraph; -} - -Box ^ _isGraphingModeEnabledCached = nullptr; -bool IsGraphingModeEnabled(User ^ currentUser = nullptr) -{ - if (!IsGraphingModeAvailable()) + bool IsGraphingModeEnabled() { - return false; - } + static bool isChecked = false; + static bool isEnabled = false; - if (_isGraphingModeEnabledCached != nullptr) - { - return _isGraphingModeEnabledCached->Value; - } - - if (!currentUser) - { - return true; - } - - auto namedPolicyData = NamedPolicy::GetPolicyFromPathForUser(currentUser, L"Education", L"AllowGraphingCalculator"); - _isGraphingModeEnabledCached = namedPolicyData->GetBoolean() == true; - - return _isGraphingModeEnabledCached->Value; -} - -// The order of items in this list determines the order of items in the menu. -static list s_categoryManifest = [] { - auto res = list{ NavCategoryInitializer{ ViewMode::Standard, - STANDARD_ID, - L"Standard", - L"StandardMode", - L"\uE8EF", - CategoryGroupType::Calculator, - MyVirtualKey::Number1, - L"1", - SUPPORTS_ALL, - true }, - NavCategoryInitializer{ ViewMode::Scientific, - SCIENTIFIC_ID, - L"Scientific", - L"ScientificMode", - L"\uF196", - CategoryGroupType::Calculator, - MyVirtualKey::Number2, - L"2", - SUPPORTS_ALL, - true } }; - - int currentIndex = 3; - bool supportGraphingCalculator = IsGraphingModeAvailable(); - if (supportGraphingCalculator) - { - bool isEnabled = IsGraphingModeEnabled(); - res.push_back(NavCategoryInitializer{ ViewMode::Graphing, - GRAPHING_ID, - L"Graphing", - L"GraphingCalculatorMode", - L"\uF770", - CategoryGroupType::Calculator, - MyVirtualKey::Number3, - L"3", - SUPPORTS_ALL, - isEnabled }); - ++currentIndex; - } - res.insert( - res.end(), - { NavCategoryInitializer{ ViewMode::Programmer, - PROGRAMMER_ID, - L"Programmer", - L"ProgrammerMode", - L"\uECCE", - CategoryGroupType::Calculator, - supportGraphingCalculator ? MyVirtualKey::Number4 : MyVirtualKey::Number3, - towchar_t(currentIndex++), - SUPPORTS_ALL, - true }, - NavCategoryInitializer{ ViewMode::Date, - DATE_ID, - L"Date", - L"DateCalculationMode", - L"\uE787", - CategoryGroupType::Calculator, - supportGraphingCalculator ? MyVirtualKey::Number5 : MyVirtualKey::Number4, - towchar_t(currentIndex++), - SUPPORTS_ALL, - true }, - NavCategoryInitializer{ ViewMode::Currency, - CURRENCY_ID, - L"Currency", - L"CategoryName_Currency", - L"\uEB0D", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Volume, - VOLUME_ID, - L"Volume", - L"CategoryName_Volume", - L"\uF1AA", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Length, - LENGTH_ID, - L"Length", - L"CategoryName_Length", - L"\uECC6", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Weight, - WEIGHT_ID, - L"Weight and Mass", - L"CategoryName_Weight", - L"\uF4C1", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Temperature, - TEMPERATURE_ID, - L"Temperature", - L"CategoryName_Temperature", - L"\uE7A3", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - SUPPORTS_NEGATIVE, - true }, - NavCategoryInitializer{ ViewMode::Energy, - ENERGY_ID, - L"Energy", - L"CategoryName_Energy", - L"\uECAD", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Area, - AREA_ID, - L"Area", - L"CategoryName_Area", - L"\uE809", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Speed, - SPEED_ID, - L"Speed", - L"CategoryName_Speed", - L"\uEADA", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Time, - TIME_ID, - L"Time", - L"CategoryName_Time", - L"\uE917", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Power, - POWER_ID, - L"Power", - L"CategoryName_Power", - L"\uE945", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - SUPPORTS_NEGATIVE, - true }, - NavCategoryInitializer{ ViewMode::Data, - DATA_ID, - L"Data", - L"CategoryName_Data", - L"\uF20F", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Pressure, - PRESSURE_ID, - L"Pressure", - L"CategoryName_Pressure", - L"\uEC4A", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - POSITIVE_ONLY, - true }, - NavCategoryInitializer{ ViewMode::Angle, - ANGLE_ID, - L"Angle", - L"CategoryName_Angle", - L"\uF515", - CategoryGroupType::Converter, - MyVirtualKey::None, - nullptr, - SUPPORTS_NEGATIVE, - true } }); - return res; -}(); - -void NavCategory::InitializeCategoryManifest(User ^ user) -{ - int i = 0; - for (NavCategoryInitializer category : s_categoryManifest) - { - if (category.viewMode == ViewMode::Graphing) + std::scoped_lock lock(GraphingModeCheckMutex); + if (isChecked) { - auto navCatInit = s_categoryManifest.begin(); - std::advance(navCatInit, i); - (*navCatInit).isEnabled = IsGraphingModeEnabled(user); - break; + return isEnabled; } else { - i++; - } - } -} - -// This function should only be used when storing the mode to app data. -int NavCategory::Serialize(ViewMode mode) -{ - auto iter = - find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - - return (iter != s_categoryManifest.end()) ? iter->serializationId : -1; -} - -// This function should only be used when restoring the mode from app data. -ViewMode NavCategory::Deserialize(Platform::Object ^ obj) -{ - // If we cast directly to ViewMode we will fail - // because we technically store an int. - // Need to cast to int, then ViewMode. - auto boxed = dynamic_cast ^>(obj); - if (boxed != nullptr) - { - int serializationId = boxed->Value; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [serializationId](const NavCategoryInitializer& initializer) { - return initializer.serializationId == serializationId; - }); - - if (iter != s_categoryManifest.end()) - { - if (iter->viewMode == ViewMode::Graphing) - { - // check if the user is allowed to use this feature - if (!IsGraphingModeEnabled()) - { - return ViewMode::None; - } - } - return iter->viewMode; + auto namedPolicyData = NamedPolicy::GetPolicyFromPathForUser( + CurrentUser.Get(), + L"Education", + L"AllowGraphingCalculator"); + isEnabled = namedPolicyData->GetBoolean(); + isChecked = true; + return isEnabled; } } - return ViewMode::None; -} + // The order of items in this list determines the order of items in the menu. + const std::vector s_categoryManifest { + NavCategoryInitializer{ ViewMode::Standard, + STANDARD_ID, + L"Standard", + L"StandardMode", + L"\uE8EF", + CategoryGroupType::Calculator, + MyVirtualKey::Number1, + L"1", + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Scientific, + SCIENTIFIC_ID, + L"Scientific", + L"ScientificMode", + L"\uF196", + CategoryGroupType::Calculator, + MyVirtualKey::Number2, + L"2", + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Graphing, + GRAPHING_ID, + L"Graphing", + L"GraphingCalculatorMode", + L"\uF770", + CategoryGroupType::Calculator, + MyVirtualKey::Number3, + L"3", + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Programmer, + PROGRAMMER_ID, + L"Programmer", + L"ProgrammerMode", + L"\uECCE", + CategoryGroupType::Calculator, + MyVirtualKey::Number4, + L"4", + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Date, + DATE_ID, + L"Date", + L"DateCalculationMode", + L"\uE787", + CategoryGroupType::Calculator, + MyVirtualKey::Number5, + L"5", + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Currency, + CURRENCY_ID, + L"Currency", + L"CategoryName_Currency", + L"\uEB0D", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Volume, + VOLUME_ID, + L"Volume", + L"CategoryName_Volume", + L"\uF1AA", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Length, + LENGTH_ID, + L"Length", + L"CategoryName_Length", + L"\uECC6", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Weight, + WEIGHT_ID, + L"Weight and Mass", + L"CategoryName_Weight", + L"\uF4C1", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Temperature, + TEMPERATURE_ID, + L"Temperature", + L"CategoryName_Temperature", + L"\uE7A3", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + SUPPORTS_NEGATIVE }, + NavCategoryInitializer{ ViewMode::Energy, + ENERGY_ID, + L"Energy", + L"CategoryName_Energy", + L"\uECAD", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Area, + AREA_ID, + L"Area", + L"CategoryName_Area", + L"\uE809", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Speed, + SPEED_ID, + L"Speed", + L"CategoryName_Speed", + L"\uEADA", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Time, + TIME_ID, + L"Time", + L"CategoryName_Time", + L"\uE917", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Power, + POWER_ID, + L"Power", + L"CategoryName_Power", + L"\uE945", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + SUPPORTS_NEGATIVE }, + NavCategoryInitializer{ ViewMode::Data, + DATA_ID, + L"Data", + L"CategoryName_Data", + L"\uF20F", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Pressure, + PRESSURE_ID, + L"Pressure", + L"CategoryName_Pressure", + L"\uEC4A", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + POSITIVE_ONLY }, + NavCategoryInitializer{ ViewMode::Angle, + ANGLE_ID, + L"Angle", + L"CategoryName_Angle", + L"\uF515", + CategoryGroupType::Converter, + MyVirtualKey::None, + std::nullopt, + SUPPORTS_NEGATIVE }, + }; +} // namespace unnamed -bool NavCategory::IsValidViewMode(ViewMode mode) -{ - auto iter = - find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - return iter != s_categoryManifest.end(); -} - -bool NavCategory::IsViewModeEnabled(ViewMode mode) -{ - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { - return initializer.viewMode == mode && initializer.isEnabled; - }); - - return iter != s_categoryManifest.end(); -} - -bool NavCategory::IsCalculatorViewMode(ViewMode mode) +bool NavCategory::IsCalculatorViewMode(ViewModeType mode) { // Historically, Calculator modes are Standard, Scientific, and Programmer. return !IsDateCalculatorViewMode(mode) && !IsGraphingCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator); } -bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode) +bool NavCategory::IsGraphingCalculatorViewMode(ViewModeType mode) { - return mode == ViewMode::Graphing; + return mode == ViewModeType::Graphing; } -bool NavCategory::IsDateCalculatorViewMode(ViewMode mode) +bool NavCategory::IsDateCalculatorViewMode(ViewModeType mode) { - return mode == ViewMode::Date; + return mode == ViewModeType::Date; } -bool NavCategory::IsConverterViewMode(ViewMode mode) +bool NavCategory::IsConverterViewMode(ViewModeType mode) { return IsModeInCategoryGroup(mode, CategoryGroupType::Converter); } -bool NavCategory::IsModeInCategoryGroup(ViewMode mode, CategoryGroupType type) +bool NavCategory::IsModeInCategoryGroup(ViewModeType mode, CategoryGroupType type) { - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, type](const NavCategoryInitializer& initializer) { - return initializer.viewMode == mode && initializer.groupType == type; - }); - - return iter != s_categoryManifest.end(); -} - -String ^ NavCategory::GetFriendlyName(ViewMode mode) -{ - auto iter = - find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - - return (iter != s_categoryManifest.end()) ? StringReference(iter->friendlyName) : L"None"; -} - -ViewMode NavCategory::GetViewModeForFriendlyName(String ^ name) -{ - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [name](const NavCategoryInitializer& initializer) { - return wcscmp(initializer.friendlyName, name->Data()) == 0; - }); - - return (iter != s_categoryManifest.end()) ? iter->viewMode : ViewMode::None; -} - -String ^ NavCategory::GetNameResourceKey(ViewMode mode) -{ - auto iter = - find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - - return (iter != s_categoryManifest.end()) ? StringReference(iter->nameResourceKey) + "Text" : nullptr; -} - -CategoryGroupType NavCategory::GetGroupType(ViewMode mode) -{ - auto iter = - find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode; }); - - return (iter != s_categoryManifest.end()) ? iter->groupType : CategoryGroupType::None; -} - -// GetIndex is 0-based, GetPosition is 1-based -int NavCategory::GetIndex(ViewMode mode) -{ - int position = NavCategory::GetPosition(mode); - return max(-1, position - 1); -} - -int NavCategory::GetFlatIndex(ViewMode mode) -{ - int index = -1; - CategoryGroupType type = CategoryGroupType::None; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, &type, &index](const NavCategoryInitializer& initializer) { - index++; - if (initializer.groupType != type) - { - type = initializer.groupType; - index++; - } - - return initializer.viewMode == mode; - }); - - return (iter != s_categoryManifest.end()) ? index : -1; -} - -// GetIndex is 0-based, GetPosition is 1-based -int NavCategory::GetIndexInGroup(ViewMode mode, CategoryGroupType type) -{ - int index = -1; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, type, &index](const NavCategoryInitializer& initializer) { - if (initializer.groupType == type) - { - index++; - return initializer.viewMode == mode; - } - - return false; - }); - - return (iter != s_categoryManifest.end()) ? index : -1; -} - -// GetIndex is 0-based, GetPosition is 1-based -int NavCategory::GetPosition(ViewMode mode) -{ - int position = 0; - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode, &position](const NavCategoryInitializer& initializer) { - position++; - return initializer.viewMode == mode; - }); - - return (iter != s_categoryManifest.end()) ? position : -1; -} - -ViewMode NavCategory::GetViewModeForVirtualKey(MyVirtualKey virtualKey) -{ - auto iter = find_if(begin(s_categoryManifest), end(s_categoryManifest), [virtualKey](const NavCategoryInitializer& initializer) { - return initializer.virtualKey == virtualKey; - }); - - return (iter != s_categoryManifest.end()) ? iter->viewMode : ViewMode::None; -} - -void NavCategory::GetCategoryAcceleratorKeys(IVector ^ accelerators) -{ - if (accelerators != nullptr) - { - accelerators->Clear(); - for (auto category : s_categoryManifest) - { - if (category.virtualKey != MyVirtualKey::None) - { - accelerators->Append(category.virtualKey); - } - } - } + return std::any_of( + s_categoryManifest.cbegin(), + s_categoryManifest.cend(), + [mode, type](const auto& initializer) { + return initializer.viewMode == mode && initializer.groupType == type; + }); } NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer) @@ -523,33 +300,225 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti categoryName, categoryAutomationName, StringReference(categoryInitializer.glyph), - categoryInitializer.accessKey != nullptr ? ref new String(categoryInitializer.accessKey) + categoryInitializer.accessKey.has_value() ? ref new String(categoryInitializer.accessKey->c_str()) : resProvider->GetResourceString(nameResourceKey + "AccessKey"), groupMode, categoryInitializer.viewMode, categoryInitializer.supportsNegative, - categoryInitializer.isEnabled)); + categoryInitializer.viewMode != ViewMode::Graphing)); } } } -IObservableVector ^ NavCategoryGroup::CreateMenuOptions() +void NavCategoryStates::SetCurrentUser(Windows::System::User^ user) +{ + std::scoped_lock lock(GraphingModeCheckMutex); + CurrentUser = user; +} + +IObservableVector ^ NavCategoryStates::CreateMenuOptions() { auto menuOptions = ref new Vector(); - menuOptions->Append(CreateCalculatorCategory()); - menuOptions->Append(CreateConverterCategory()); + menuOptions->Append(CreateCalculatorCategoryGroup()); + menuOptions->Append(CreateConverterCategoryGroup()); return menuOptions; } -NavCategoryGroup ^ NavCategoryGroup::CreateCalculatorCategory() +NavCategoryGroup ^ NavCategoryStates::CreateCalculatorCategoryGroup() { return ref new NavCategoryGroup( NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" }); } -NavCategoryGroup ^ NavCategoryGroup::CreateConverterCategory() +NavCategoryGroup ^ NavCategoryStates::CreateConverterCategoryGroup() { return ref new NavCategoryGroup( NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" }); } +// This function should only be used when storing the mode to app data. +int NavCategoryStates::Serialize(ViewMode mode) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode](const auto& initializer) { return initializer.viewMode == mode; }); + + return (citer != s_categoryManifest.cend()) ? citer->serializationId : -1; +} + +// This function should only be used when restoring the mode from app data. +ViewMode NavCategoryStates::Deserialize(Platform::Object ^ obj) +{ + // If we cast directly to ViewMode we will fail + // because we technically store an int. + // Need to cast to int, then ViewMode. + auto boxed = dynamic_cast ^>(obj); + if (boxed != nullptr) + { + int serializationId = boxed->Value; + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [serializationId](const auto& initializer) { return initializer.serializationId == serializationId; }); + + return citer != s_categoryManifest.cend() ? + (citer->viewMode == ViewMode::Graphing ? + (IsGraphingModeEnabled() ? citer->viewMode : ViewMode::None) + : citer->viewMode) + : ViewMode::None; + } + else + { + return ViewMode::None; + } +} + +ViewMode NavCategoryStates::GetViewModeForFriendlyName(String ^ name) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [name](const auto& initializer) { return wcscmp(initializer.friendlyName, name->Data()) == 0; }); + + return (citer != s_categoryManifest.cend()) ? citer->viewMode : ViewMode::None; +} + +String ^ NavCategoryStates::GetFriendlyName(ViewMode mode) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode](const auto& initializer) { return initializer.viewMode == mode; }); + + return (citer != s_categoryManifest.cend()) ? StringReference(citer->friendlyName) : L"None"; +} + +String ^ NavCategoryStates::GetNameResourceKey(ViewMode mode) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode](const auto& initializer) { return initializer.viewMode == mode; }); + + return (citer != s_categoryManifest.cend()) ? StringReference(citer->nameResourceKey) + "Text" : nullptr; +} + +CategoryGroupType NavCategoryStates::GetGroupType(ViewMode mode) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode](const auto& initializer) { return initializer.viewMode == mode; }); + + return (citer != s_categoryManifest.cend()) ? citer->groupType : CategoryGroupType::None; +} + +// GetIndex is 0-based, GetPosition is 1-based +int NavCategoryStates::GetIndex(ViewMode mode) +{ + int position = GetPosition(mode); + return std::max(-1, position - 1); +} + +int NavCategoryStates::GetFlatIndex(ViewMode mode) +{ + int index = -1; + CategoryGroupType type = CategoryGroupType::None; + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode, &type, &index](const auto& initializer) { + ++index; + if (initializer.groupType != type) + { + type = initializer.groupType; + ++index; + } + return initializer.viewMode == mode; + }); + + return (citer != s_categoryManifest.cend()) ? index : -1; +} + +// GetIndex is 0-based, GetPosition is 1-based +int NavCategoryStates::GetIndexInGroup(ViewMode mode, CategoryGroupType type) +{ + int index = -1; + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode, type, &index](const auto& initializer) { + if (initializer.groupType == type) + { + ++index; + return initializer.viewMode == mode; + } + return false; + }); + + return (citer != s_categoryManifest.cend()) ? index : -1; +} + +// GetIndex is 0-based, GetPosition is 1-based +int NavCategoryStates::GetPosition(ViewMode mode) +{ + int position = 0; + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode, &position](const auto& initializer) { + ++position; + return initializer.viewMode == mode; + }); + + return (citer != s_categoryManifest.cend()) ? position : -1; +} + +ViewMode NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey virtualKey) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [virtualKey](const auto& initializer) { return initializer.virtualKey == virtualKey; }); + + return (citer != s_categoryManifest.end()) ? citer->viewMode : ViewMode::None; +} + +void NavCategoryStates::GetCategoryAcceleratorKeys(IVector ^ accelerators) +{ + if (accelerators != nullptr) + { + accelerators->Clear(); + for (const auto& category : s_categoryManifest) + { + if (category.virtualKey != MyVirtualKey::None) + { + accelerators->Append(category.virtualKey); + } + } + } +} + +bool NavCategoryStates::IsValidViewMode(ViewMode mode) +{ + const auto& citer = find_if( + cbegin(s_categoryManifest), + cend(s_categoryManifest), + [mode](const auto& initializer) { return initializer.viewMode == mode; }); + + return citer != s_categoryManifest.cend(); +} + +bool NavCategoryStates::IsViewModeEnabled(ViewMode mode) +{ + if (mode != ViewMode::Graphing) + { + return true; + } + else + { + return IsGraphingModeEnabled(); + } +} + diff --git a/src/CalcViewModel/Common/NavCategory.h b/src/CalcViewModel/Common/NavCategory.h index 1af0467..15ef098 100644 --- a/src/CalcViewModel/Common/NavCategory.h +++ b/src/CalcViewModel/Common/NavCategory.h @@ -59,46 +59,21 @@ namespace CalculatorApp::ViewModel private struct NavCategoryInitializer { - constexpr NavCategoryInitializer( - ViewMode mode, - int id, - wchar_t const* name, - wchar_t const* nameKey, - wchar_t const* glyph, - CategoryGroupType group, - MyVirtualKey vKey, - wchar_t const* aKey, - bool categorySupportsNegative, - bool enabled) - : viewMode(mode) - , serializationId(id) - , friendlyName(name) - , nameResourceKey(nameKey) - , glyph(glyph) - , groupType(group) - , virtualKey(vKey) - , accessKey(aKey) - , supportsNegative(categorySupportsNegative) - , isEnabled(enabled) - { - } - - const ViewMode viewMode; - const int serializationId; - const wchar_t* const friendlyName; - const wchar_t* const nameResourceKey; - const wchar_t* const glyph; - const CategoryGroupType groupType; - const MyVirtualKey virtualKey; - const wchar_t* const accessKey; - const bool supportsNegative; - bool isEnabled; + ViewMode viewMode; + int serializationId; + const wchar_t* friendlyName; + const wchar_t* nameResourceKey; + const wchar_t* glyph; + CategoryGroupType groupType; + MyVirtualKey virtualKey; + std::optional accessKey; + bool supportsNegative; }; private struct NavCategoryGroupInitializer { - constexpr NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const* h, wchar_t const* n, wchar_t const* a) + NavCategoryGroupInitializer(CategoryGroupType t, wchar_t const* h, wchar_t const* n, wchar_t const* a) : type(t) , headerResourceKey(h) , modeResourceKey(n) @@ -112,36 +87,85 @@ namespace CalculatorApp::ViewModel const wchar_t* automationResourceKey; }; - [Windows::UI::Xaml::Data::Bindable] public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + [Windows::UI::Xaml::Data::Bindable] + public ref class NavCategory sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { + private: + using ViewModeType = ::CalculatorApp::ViewModel::Common::ViewMode; public: OBSERVABLE_OBJECT(); PROPERTY_R(Platform::String ^, Name); PROPERTY_R(Platform::String ^, AutomationName); PROPERTY_R(Platform::String ^, Glyph); - PROPERTY_R(ViewMode, Mode); + PROPERTY_R(ViewModeType, ViewMode); PROPERTY_R(Platform::String ^, AccessKey); PROPERTY_R(bool, SupportsNegative); - PROPERTY_R(bool, IsEnabled); + PROPERTY_RW(bool, IsEnabled); property Platform::String - ^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } } + ^ AutomationId { Platform::String ^ get() { return m_ViewMode.ToString(); } } + static bool IsCalculatorViewMode(ViewModeType mode); + static bool IsGraphingCalculatorViewMode(ViewModeType mode); + static bool IsDateCalculatorViewMode(ViewModeType mode); + static bool IsConverterViewMode(ViewModeType mode); + + internal : NavCategory( + Platform::String ^ name, + Platform::String ^ automationName, + Platform::String ^ glyph, + Platform::String ^ accessKey, + Platform::String ^ mode, + ViewModeType viewMode, + bool supportsNegative, + bool isEnabled) + : m_Name(name) + , m_AutomationName(automationName) + , m_Glyph(glyph) + , m_AccessKey(accessKey) + , m_modeString(mode) + , m_ViewMode(viewMode) + , m_SupportsNegative(supportsNegative) + , m_IsEnabled(isEnabled) + { + } + + private: + static bool IsModeInCategoryGroup(ViewModeType mode, CategoryGroupType groupType); + + Platform::String ^ m_modeString; + }; + + [Windows::UI::Xaml::Data::Bindable] + public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + internal: + NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer); + public: + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(Platform::String ^, Name); + OBSERVABLE_PROPERTY_R(Platform::String ^, AutomationName); + OBSERVABLE_PROPERTY_R(CategoryGroupType, GroupType); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Categories); + }; + + public ref class NavCategoryStates sealed + { + public: + static void SetCurrentUser(Windows::System::User^ user); + static Windows::Foundation::Collections::IObservableVector ^ CreateMenuOptions(); + static NavCategoryGroup ^ CreateCalculatorCategoryGroup(); + static NavCategoryGroup ^ CreateConverterCategoryGroup(); + + static bool IsValidViewMode(ViewMode mode); + static bool IsViewModeEnabled(ViewMode mode); // For saving/restoring last mode used. static int Serialize(ViewMode mode); static ViewMode Deserialize(Platform::Object ^ obj); + + // Query properties from states static ViewMode GetViewModeForFriendlyName(Platform::String ^ name); - - static bool IsValidViewMode(ViewMode mode); - static bool IsViewModeEnabled(ViewMode mode); - static bool IsCalculatorViewMode(ViewMode mode); - static bool IsGraphingCalculatorViewMode(ViewMode mode); - static bool IsDateCalculatorViewMode(ViewMode mode); - static bool IsConverterViewMode(ViewMode mode); - - static void InitializeCategoryManifest(Windows::System::User ^ user); - static Platform::String ^ GetFriendlyName(ViewMode mode); static Platform::String ^ GetNameResourceKey(ViewMode mode); static CategoryGroupType GetGroupType(ViewMode mode); @@ -152,51 +176,9 @@ namespace CalculatorApp::ViewModel static int GetIndexInGroup(ViewMode mode, CategoryGroupType type); static int GetPosition(ViewMode mode); + // Virtual key related static ViewMode GetViewModeForVirtualKey(MyVirtualKey virtualKey); static void GetCategoryAcceleratorKeys(Windows::Foundation::Collections::IVector ^ resutls); - - internal : NavCategory( - Platform::String ^ name, - Platform::String ^ automationName, - Platform::String ^ glyph, - Platform::String ^ accessKey, - Platform::String ^ mode, - ViewMode viewMode, - bool supportsNegative, - bool isEnabled) - : m_Name(name) - , m_AutomationName(automationName) - , m_Glyph(glyph) - , m_AccessKey(accessKey) - , m_modeString(mode) - , m_Mode(viewMode) - , m_SupportsNegative(supportsNegative) - , m_IsEnabled(isEnabled) - { - } - - private: - static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType); - - Platform::String ^ m_modeString; - }; - - [Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged - { - public: - OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_R(Platform::String ^, Name); - OBSERVABLE_PROPERTY_R(Platform::String ^, AutomationName); - OBSERVABLE_PROPERTY_R(CategoryGroupType, GroupType); - OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Categories); - - static Windows::Foundation::Collections::IObservableVector ^ CreateMenuOptions(); - - static NavCategoryGroup ^ CreateCalculatorCategory(); - static NavCategoryGroup ^ CreateConverterCategory(); - - private: - NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer); }; } } diff --git a/src/CalcViewModel/Common/TraceLogger.cpp b/src/CalcViewModel/Common/TraceLogger.cpp index d19f4e0..d565f24 100644 --- a/src/CalcViewModel/Common/TraceLogger.cpp +++ b/src/CalcViewModel/Common/TraceLogger.cpp @@ -79,7 +79,7 @@ namespace CalculatorApp { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddString(StringReference(L"VisualState"), state); fields->AddBoolean(StringReference(L"IsAlwaysOnTop"), isAlwaysOnTop); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VISUAL_STATE_CHANGED), fields); @@ -94,18 +94,18 @@ namespace CalculatorApp } auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddUInt64(StringReference(L"NumOfOpenWindows"), currentWindowCount); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_WINDOW_ON_CREATED), fields); } void TraceLogger::LogModeChange(ViewMode mode) { - if (NavCategory::IsValidViewMode(mode)) + if (NavCategoryStates::IsValidViewMode(mode)) { auto fields = ref new LoggingFields(); ; - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MODE_CHANGED), fields); } } @@ -113,7 +113,7 @@ namespace CalculatorApp void TraceLogger::LogHistoryItemLoad(ViewMode mode, int historyListSize, int loadedIndex) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddInt32(StringReference(L"HistoryListSize"), historyListSize); fields->AddInt32(StringReference(L"HistoryItemIndex"), loadedIndex); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_HISTORY_ITEM_LOAD), fields); @@ -122,7 +122,7 @@ namespace CalculatorApp void TraceLogger::LogMemoryItemLoad(ViewMode mode, int memoryListSize, int loadedIndex) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddInt32(StringReference(L"MemoryListSize"), memoryListSize); fields->AddInt32(StringReference(L"MemoryItemIndex"), loadedIndex); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MEMORY_ITEM_LOAD), fields); @@ -131,7 +131,7 @@ namespace CalculatorApp void TraceLogger::LogError(ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddString(StringReference(L"FunctionName"), functionName); fields->AddString(StringReference(L"Message"), errorString); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); @@ -140,7 +140,7 @@ namespace CalculatorApp void TraceLogger::LogStandardException(ViewMode mode, wstring_view functionName, const exception& e) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddString(StringReference(L"FunctionName"), StringReference(functionName.data())); wstringstream exceptionMessage; exceptionMessage << e.what(); @@ -151,7 +151,7 @@ namespace CalculatorApp void TraceLogger::LogPlatformExceptionInfo(CalculatorApp::ViewModel::Common::ViewMode mode, Platform::String ^ functionName, Platform::String^ message, int hresult) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); fields->AddString(StringReference(L"FunctionName"), functionName); fields->AddString(StringReference(L"Message"), message); fields->AddInt32(StringReference(L"HRESULT"), hresult); @@ -223,7 +223,7 @@ namespace CalculatorApp Platform::String ^ buttonUsageString; for (size_t i = 0; i < buttonLog.size(); i++) { - buttonUsageString += NavCategory::GetFriendlyName(buttonLog[i].mode); + buttonUsageString += NavCategoryStates::GetFriendlyName(buttonLog[i].mode); buttonUsageString += "|"; buttonUsageString += buttonLog[i].button.ToString(); buttonUsageString += "|"; @@ -245,7 +245,7 @@ namespace CalculatorApp { const wchar_t* calculationType = AddSubtractMode ? L"AddSubtractMode" : L"DateDifferenceMode"; auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(ViewMode::Date)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(ViewMode::Date)); fields->AddString(StringReference(L"CalculationType"), StringReference(calculationType)); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_DATE_CALCULATION_MODE_USED), fields); } @@ -253,7 +253,7 @@ namespace CalculatorApp void TraceLogger::LogConverterInputReceived(ViewMode mode) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_CONVERTER_INPUT_RECEIVED), fields); } @@ -266,7 +266,7 @@ namespace CalculatorApp void TraceLogger::LogInputPasted(ViewMode mode) { auto fields = ref new LoggingFields(); - fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(CALC_MODE), NavCategoryStates::GetFriendlyName(mode)); TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_INPUT_PASTED), fields); } diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp index 91429e6..c0b3690 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp @@ -240,7 +240,7 @@ unordered_map CurrencyDataLoader: bool CurrencyDataLoader::SupportsCategory(const UCM::Category& target) { - static int currencyId = NavCategory::Serialize(ViewMode::Currency); + static int currencyId = NavCategoryStates::Serialize(ViewMode::Currency); return target.id == currencyId; } diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp index 564bf31..3ce1cb6 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp @@ -53,7 +53,7 @@ bool UnitConverterDataLoader::SupportsCategory(const UCM::Category& target) GetCategories(supportedCategories); } - static int currencyId = NavCategory::Serialize(ViewMode::Currency); + static int currencyId = NavCategoryStates::Serialize(ViewMode::Currency); auto itr = find_if(supportedCategories->begin(), supportedCategories->end(), [&](const UCM::Category& category) { return currencyId != category.id && target.id == category.id; }); @@ -79,7 +79,7 @@ void UnitConverterDataLoader::LoadData() this->m_ratioMap->clear(); for (UCM::Category objectCategory : *m_categoryList) { - ViewMode categoryViewMode = NavCategory::Deserialize(objectCategory.id); + ViewMode categoryViewMode = NavCategoryStates::Deserialize(objectCategory.id); assert(NavCategory::IsConverterViewMode(categoryViewMode)); if (categoryViewMode == ViewMode::Currency) { @@ -148,11 +148,11 @@ void UnitConverterDataLoader::LoadData() void UnitConverterDataLoader::GetCategories(_In_ shared_ptr> categoriesList) { categoriesList->clear(); - auto converterCategory = NavCategoryGroup::CreateConverterCategory(); + auto converterCategory = NavCategoryStates::CreateConverterCategoryGroup(); for (auto const& category : converterCategory->Categories) { /* Id, CategoryName, SupportsNegative */ - categoriesList->emplace_back(NavCategory::Serialize(category->Mode), category->Name->Data(), category->SupportsNegative); + categoriesList->emplace_back(NavCategoryStates::Serialize(category->ViewMode), category->Name->Data(), category->SupportsNegative); } } diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index caeb5bb..cbd49b6 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -719,7 +719,7 @@ void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter) } // Ensure that the paste happens on the UI thread - create_task(CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), numberBase, bitLengthType)) + create_task(CopyPasteManager::GetStringToPaste(mode, NavCategoryStates::GetGroupType(mode), numberBase, bitLengthType)) .then([that, mode](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } diff --git a/src/CalcViewModel/UnitConverterViewModel.cpp b/src/CalcViewModel/UnitConverterViewModel.cpp index 1746623..4784853 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -537,7 +537,7 @@ void UnitConverterViewModel::OnPasteCommand(Platform::Object ^ parameter) // Any converter ViewMode is fine here. auto that(this); - create_task(CopyPasteManager::GetStringToPaste(m_Mode, NavCategory::GetGroupType(m_Mode), NumberBase::Unknown, BitLength::BitLengthUnknown)) + create_task(CopyPasteManager::GetStringToPaste(m_Mode, NavCategoryStates::GetGroupType(m_Mode), NumberBase::Unknown, BitLength::BitLengthUnknown)) .then([that](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } diff --git a/src/CalcViewModel/UnitConverterViewModel.h b/src/CalcViewModel/UnitConverterViewModel.h index 594742e..64c7516 100644 --- a/src/CalcViewModel/UnitConverterViewModel.h +++ b/src/CalcViewModel/UnitConverterViewModel.h @@ -203,7 +203,7 @@ namespace CalculatorApp if (value != nullptr) { auto currentCategory = value->GetModelCategory(); - IsCurrencyCurrentCategory = currentCategory.id == CalculatorApp::ViewModel::Common::NavCategory::Serialize(CalculatorApp::ViewModel::Common::ViewMode::Currency); + IsCurrencyCurrentCategory = currentCategory.id == CalculatorApp::ViewModel::Common::NavCategoryStates::Serialize(CalculatorApp::ViewModel::Common::ViewMode::Currency); } RaisePropertyChanged("CurrentCategory"); } diff --git a/src/Calculator/App.xaml.cs b/src/Calculator/App.xaml.cs index a458695..c18e76a 100644 --- a/src/Calculator/App.xaml.cs +++ b/src/Calculator/App.xaml.cs @@ -85,7 +85,11 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) // If the app got pre-launch activated, then save that state in a flag m_preLaunched = true; } - NavCategory.InitializeCategoryManifest(args.User); + NavCategoryStates.SetCurrentUser(args.User); + + // It takes time to check GraphingMode at the 1st time. So, do it in a background thread + Task.Run(() => NavCategoryStates.IsViewModeEnabled(ViewMode.Graphing)); + OnAppLaunch(args, args.Arguments); } @@ -403,7 +407,7 @@ private async Task SetupJumpList() { try { - var calculatorOptions = NavCategoryGroup.CreateCalculatorCategory(); + var calculatorOptions = NavCategoryStates.CreateCalculatorCategoryGroup(); var jumpList = await JumpList.LoadCurrentAsync(); jumpList.SystemGroupKind = JumpListSystemGroupKind.None; @@ -411,13 +415,13 @@ private async Task SetupJumpList() foreach (NavCategory option in calculatorOptions.Categories) { - if (!option.IsEnabled) + if (!NavCategoryStates.IsViewModeEnabled(option.ViewMode)) { continue; } - ViewMode mode = option.Mode; - var item = JumpListItem.CreateWithArguments(((int)mode).ToString(), "ms-resource:///Resources/" + NavCategory.GetNameResourceKey(mode)); - item.Description = "ms-resource:///Resources/" + NavCategory.GetNameResourceKey(mode); + ViewMode mode = option.ViewMode; + var item = JumpListItem.CreateWithArguments(((int)mode).ToString(), "ms-resource:///Resources/" + NavCategoryStates.GetNameResourceKey(mode)); + item.Description = "ms-resource:///Resources/" + NavCategoryStates.GetNameResourceKey(mode); item.Logo = new Uri("ms-appx:///Assets/" + mode.ToString() + ".png"); jumpList.Items.Add(item); diff --git a/src/Calculator/Calculator.csproj b/src/Calculator/Calculator.csproj index 461a93e..65942a8 100644 --- a/src/Calculator/Calculator.csproj +++ b/src/Calculator/Calculator.csproj @@ -165,11 +165,12 @@ + EquationStylePanelControl.xaml - + Calculator.xaml @@ -235,7 +236,7 @@ Settings.xaml - + CalculatorProgrammerDisplayPanel.xaml @@ -773,7 +774,7 @@ Designer MSBuild:Compile - + Designer MSBuild:Compile diff --git a/src/Calculator/Common/KeyboardShortcuManager.cs b/src/Calculator/Common/KeyboardShortcuManager.cs index e8cb3fc..224ba23 100644 --- a/src/Calculator/Common/KeyboardShortcuManager.cs +++ b/src/Calculator/Common/KeyboardShortcuManager.cs @@ -579,7 +579,7 @@ private static bool CanNavigateModeByShortcut(MUXC.NavigationView navView, MUXC. , ApplicationViewModel vm, ViewMode toMode) { return nvi != null && nvi.IsEnabled && navView.Visibility == Visibility.Visible - && !vm.IsAlwaysOnTop && NavCategory.IsValidViewMode(toMode); + && !vm.IsAlwaysOnTop && NavCategoryStates.IsValidViewMode(toMode); } private static void NavigateModeByShortcut(bool controlKeyPressed, bool shiftKeyPressed, bool altPressed @@ -602,8 +602,8 @@ private static void NavigateModeByShortcut(bool controlKeyPressed, bool shiftKey var vm = (navView.DataContext as ApplicationViewModel); if (null != vm) { - ViewMode realToMode = toMode.HasValue ? toMode.Value : NavCategory.GetViewModeForVirtualKey(((MyVirtualKey)key)); - var nvi = (menuItems[NavCategory.GetFlatIndex(realToMode)] as MUXC.NavigationViewItem); + ViewMode realToMode = toMode.HasValue ? toMode.Value : NavCategoryStates.GetViewModeForVirtualKey(((MyVirtualKey)key)); + var nvi = (menuItems[NavCategoryStates.GetFlatIndex(realToMode)] as MUXC.NavigationViewItem); if (CanNavigateModeByShortcut(navView, nvi, vm, realToMode)) { vm.Mode = realToMode; diff --git a/src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesTemplateSelector.cs b/src/Calculator/Selectors/KeyGraphFeaturesTemplateSelector.cs similarity index 100% rename from src/Calculator/Views/GraphingCalculator/KeyGraphFeaturesTemplateSelector.cs rename to src/Calculator/Selectors/KeyGraphFeaturesTemplateSelector.cs diff --git a/src/Calculator/Selectors/NavViewMenuItemTemplateSelector.cs b/src/Calculator/Selectors/NavViewMenuItemTemplateSelector.cs new file mode 100644 index 0000000..a5e9a81 --- /dev/null +++ b/src/Calculator/Selectors/NavViewMenuItemTemplateSelector.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using CalculatorApp.ViewModel.Common; +using System; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; + +namespace CalculatorApp.TemplateSelectors +{ + internal sealed class NavViewMenuItemTemplateSelector : DataTemplateSelector + { + public DataTemplate CategoryItemTemplate { get; set; } + public DataTemplate CategoryGroupItemTemplate { get; set; } + + protected override DataTemplate SelectTemplateCore(object item) + { + if (item is NavCategory) + { + return CategoryItemTemplate; + } + else if (item is NavCategoryGroup) + { + return CategoryGroupItemTemplate; + } + else + { + throw new NotSupportedException($"typeof(item) must be {nameof(NavCategory)} or {nameof(NavCategoryGroup)}."); + } + } + + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + return SelectTemplateCore(item); + } + } +} + diff --git a/src/Calculator/Views/StateTriggers/CalculatorProgrammerDisplayPanel.xaml b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml similarity index 100% rename from src/Calculator/Views/StateTriggers/CalculatorProgrammerDisplayPanel.xaml rename to src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml diff --git a/src/Calculator/Views/StateTriggers/CalculatorProgrammerDisplayPanel.xaml.cs b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cs similarity index 100% rename from src/Calculator/Views/StateTriggers/CalculatorProgrammerDisplayPanel.xaml.cs rename to src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cs diff --git a/src/Calculator/Views/MainPage.xaml b/src/Calculator/Views/MainPage.xaml index 8fac3ea..be31db1 100644 --- a/src/Calculator/Views/MainPage.xaml +++ b/src/Calculator/Views/MainPage.xaml @@ -7,6 +7,8 @@ xmlns:local="using:CalculatorApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" + xmlns:selectors="using:CalculatorApp.TemplateSelectors" + xmlns:vmcom="using:CalculatorApp.ViewModel.Common" x:Name="PageRoot" muxc:BackdropMaterial.ApplyToRootOrPageBackground="True" Loaded="OnPageLoaded" @@ -25,6 +27,27 @@ + + + + + + + + + + + @@ -96,7 +119,8 @@ IsSettingsVisible="True" ItemInvoked="OnNavItemInvoked" Loaded="OnNavLoaded" - MenuItemsSource="{x:Bind CreateUIElementsForCategories(Model.Categories), Mode=OneWay}" + MenuItemsSource="{x:Bind NavViewCategoriesSource, Mode=OneWay}" + MenuItemTemplateSelector="{StaticResource NavViewMenuItemTemplateSelector}" OpenPaneLength="{x:Bind NavigationViewOpenPaneLength(Model.IsAlwaysOnTop), Mode=OneWay}" PaneDisplayMode="LeftMinimal" PaneClosed="OnNavPaneClosed" diff --git a/src/Calculator/Views/MainPage.xaml.cs b/src/Calculator/Views/MainPage.xaml.cs index 04c68a1..d8b5f24 100644 --- a/src/Calculator/Views/MainPage.xaml.cs +++ b/src/Calculator/Views/MainPage.xaml.cs @@ -5,10 +5,8 @@ using CalculatorApp.ViewModel.Common.Automation; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using Windows.Foundation; -using Windows.Foundation.Collections; using Windows.Graphics.Display; using Windows.Storage; using Windows.UI.Core; @@ -28,9 +26,21 @@ namespace CalculatorApp /// public sealed partial class MainPage : Page { + public static readonly DependencyProperty NavViewCategoriesSourceProperty = + DependencyProperty.Register(nameof(NavViewCategoriesSource), typeof(List), typeof(MainPage), new PropertyMetadata(default)); + + public List NavViewCategoriesSource + { + get { return (List)GetValue(NavViewCategoriesSourceProperty); } + set { SetValue(NavViewCategoriesSourceProperty, value); } + } + + public ApplicationViewModel Model => m_model; + public MainPage() { - m_model = new ViewModel.ApplicationViewModel(); + m_model = new ApplicationViewModel(); + InitializeNavViewCategoriesSource(); InitializeComponent(); KeyboardShortcutManager.Initialize(); @@ -48,11 +58,6 @@ public MainPage() } } - public CalculatorApp.ViewModel.ApplicationViewModel Model - { - get => m_model; - } - public void UnregisterEventHandlers() { Window.Current.SizeChanged -= WindowSizeChanged; @@ -111,23 +116,6 @@ public void SetHeaderAutomationName() AutomationProperties.SetName(Header, name); } - public ObservableCollection CreateUIElementsForCategories(IObservableVector categories) - { - var menuCategories = new ObservableCollection(); - - foreach (var group in categories) - { - menuCategories.Add(CreateNavViewHeaderFromGroup(group)); - - foreach (var category in group.Categories) - { - menuCategories.Add(CreateNavViewItemFromCategory(category)); - } - } - - return menuCategories; - } - protected override void OnNavigatedTo(NavigationEventArgs e) { ViewMode initialMode = ViewMode.Standard; @@ -142,13 +130,53 @@ protected override void OnNavigatedTo(NavigationEventArgs e) ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings; if (localSettings.Values.ContainsKey(ApplicationViewModel.ModePropertyName)) { - initialMode = NavCategory.Deserialize(localSettings.Values[ApplicationViewModel.ModePropertyName]); + initialMode = NavCategoryStates.Deserialize(localSettings.Values[ApplicationViewModel.ModePropertyName]); } } m_model.Initialize(initialMode); } + private void InitializeNavViewCategoriesSource() + { + NavViewCategoriesSource = ExpandNavViewCategoryGroups(Model.Categories); + Model.Categories.VectorChanged += (sender, args) => + { + NavViewCategoriesSource.Clear(); + NavViewCategoriesSource = ExpandNavViewCategoryGroups(Model.Categories); + }; + + _ = Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => + { + var graphCategory = (NavCategory)NavViewCategoriesSource.Find(x => + { + if(x is NavCategory category) + { + return category.ViewMode == ViewMode.Graphing; + } + else + { + return false; + } + }); + graphCategory.IsEnabled = NavCategoryStates.IsViewModeEnabled(ViewMode.Graphing); + }); + } + + private List ExpandNavViewCategoryGroups(IEnumerable groups) + { + var result = new List(); + foreach(var group in groups) + { + result.Add(group); + foreach(var category in group.Categories) + { + result.Add(category); + } + } + return result; + } + private void UpdatePopupSize(Windows.UI.Core.WindowSizeChangedEventArgs e) { if(PopupContent != null) @@ -243,9 +271,9 @@ private void OnAppPropertyChanged(object sender, PropertyChangedEventArgs e) private void SelectNavigationItemByModel() { - var menuItems = ((ObservableCollection)NavView.MenuItemsSource); - var itemCount = ((int)menuItems.Count); - var flatIndex = NavCategory.GetFlatIndex(Model.Mode); + var menuItems = (List)NavView.MenuItemsSource; + var itemCount = menuItems.Count; + var flatIndex = NavCategoryStates.GetFlatIndex(Model.Mode); if (flatIndex >= 0 && flatIndex < itemCount) { @@ -261,7 +289,7 @@ private void OnNavLoaded(object sender, RoutedEventArgs e) } var acceleratorList = new List(); - NavCategory.GetCategoryAcceleratorKeys(acceleratorList); + NavCategoryStates.GetCategoryAcceleratorKeys(acceleratorList); foreach (var accelerator in acceleratorList) { @@ -340,8 +368,7 @@ private void OnNavSelectionChanged(object sender, MUXC.NavigationViewSelectionCh var item = (e.SelectedItemContainer as MUXC.NavigationViewItem); if (item != null) { - var selectedItem = ((NavCategory)item.DataContext); - Model.Mode = selectedItem.Mode; + Model.Mode = (ViewMode)item.Tag; } } @@ -361,39 +388,6 @@ private void TitleBarAlwaysOnTopButtonClick(object sender, RoutedEventArgs e) Model.ToggleAlwaysOnTop((float)bounds.Width, (float)bounds.Height); } - private MUXC.NavigationViewItemHeader CreateNavViewHeaderFromGroup(NavCategoryGroup group) - { - var header = new MUXC.NavigationViewItemHeader(); - header.DataContext = group; - - header.Content = group.Name; - AutomationProperties.SetName(header, group.AutomationName); - AutomationProperties.SetHeadingLevel(header, Windows.UI.Xaml.Automation.Peers.AutomationHeadingLevel.Level1); - - return header; - } - - private MUXC.NavigationViewItem CreateNavViewItemFromCategory(NavCategory category) - { - var item = new MUXC.NavigationViewItem(); - item.DataContext = category; - - var icon = new FontIcon(); - icon.FontFamily = (Windows.UI.Xaml.Media.FontFamily)(App.Current.Resources["CalculatorFontFamily"]); - icon.Glyph = category.Glyph; - item.Icon = icon; - - item.Content = category.Name; - item.AccessKey = category.AccessKey; - item.IsEnabled = category.IsEnabled; - item.Style = (Windows.UI.Xaml.Style)(Resources["NavViewItemStyle"]); - - AutomationProperties.SetName(item, category.AutomationName); - AutomationProperties.SetAutomationId(item, category.AutomationId); - - return item; - } - private void ShowHideControls(ViewMode mode) { var isCalcViewMode = NavCategory.IsCalculatorViewMode(mode); @@ -431,7 +425,7 @@ private void UpdateViewState() // All layout related view states are now handled only inside individual controls (standard, scientific, programmer, date, converter) if (NavCategory.IsConverterViewMode(m_model.Mode)) { - int modeIndex = NavCategory.GetIndexInGroup(m_model.Mode, CategoryGroupType.Converter); + int modeIndex = NavCategoryStates.GetIndexInGroup(m_model.Mode, CategoryGroupType.Converter); m_model.ConverterViewModel.CurrentCategory = m_model.ConverterViewModel.Categories[modeIndex]; } } @@ -598,11 +592,11 @@ private void Settings_BackButtonClick(object sender, RoutedEventArgs e) CloseSettingsPopup(); } - private CalculatorApp.Calculator m_calculator; + private Calculator m_calculator; private GraphingCalculator m_graphingCalculator; - private CalculatorApp.UnitConverter m_converter; - private CalculatorApp.DateCalculator m_dateCalculator; - private CalculatorApp.ViewModel.ApplicationViewModel m_model; - private Windows.UI.ViewManagement.AccessibilitySettings m_accessibilitySettings; + private UnitConverter m_converter; + private DateCalculator m_dateCalculator; + private ApplicationViewModel m_model; + private AccessibilitySettings m_accessibilitySettings; } } diff --git a/src/Calculator/Views/UnitConverter.xaml.cs b/src/Calculator/Views/UnitConverter.xaml.cs index d7db2fb..a787372 100644 --- a/src/Calculator/Views/UnitConverter.xaml.cs +++ b/src/Calculator/Views/UnitConverter.xaml.cs @@ -392,7 +392,7 @@ private void SupplementaryResultsPanelInGrid_SizeChanged(object sender, Windows. private void OnVisualStateChanged(object sender, Windows.UI.Xaml.VisualStateChangedEventArgs e) { - var mode = NavCategory.Deserialize(Model.CurrentCategory.GetModelCategoryId()); + var mode = NavCategoryStates.Deserialize(Model.CurrentCategory.GetModelCategoryId()); TraceLogger.GetInstance().LogVisualStateChanged(mode, e.NewState.Name, false); } diff --git a/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp b/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp index 4a76b48..54f5193 100644 --- a/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp +++ b/src/CalculatorUnitTests/CurrencyConverterUnitTests.cpp @@ -382,7 +382,7 @@ TEST_METHOD(Load_Success_LoadedFromWeb) TEST_CLASS(CurrencyConverterUnitTests) { - const UCM::Category CURRENCY_CATEGORY = { NavCategory::Serialize(ViewMode::Currency), L"Currency", false /*supportsNegative*/ }; + const UCM::Category CURRENCY_CATEGORY = { NavCategoryStates::Serialize(ViewMode::Currency), L"Currency", false /*supportsNegative*/ }; const UCM::Unit GetUnit(const vector& unitList, const wstring& target) { diff --git a/src/CalculatorUnitTests/MultiWindowUnitTests.cpp b/src/CalculatorUnitTests/MultiWindowUnitTests.cpp index 02eec51..2333672 100644 --- a/src/CalculatorUnitTests/MultiWindowUnitTests.cpp +++ b/src/CalculatorUnitTests/MultiWindowUnitTests.cpp @@ -735,8 +735,8 @@ TEST_METHOD(MultipleConverterModeCalculationTest) IObservableVector ^ unitsList[3]; // viewModels 0 & 1 have same category(Volume) and viewModel 2 has different category(Length) - int volumeIndex = NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter); - int lengthIndex = NavCategory::GetIndexInGroup(ViewMode::Length, CategoryGroupType::Converter); + int volumeIndex = NavCategoryStates::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter); + int lengthIndex = NavCategoryStates::GetIndexInGroup(ViewMode::Length, CategoryGroupType::Converter); viewModels[0]->CurrentCategory = categoryList->GetAt(volumeIndex); viewModels[1]->CurrentCategory = categoryList->GetAt(volumeIndex); viewModels[2]->CurrentCategory = categoryList->GetAt(lengthIndex); @@ -814,7 +814,7 @@ TEST_METHOD(TestStandardUnitConverterAndDateViewModels) dateCalcViewModel->IsAddMode = true; // Initialize Unit Converter - int volumeCategoryIndex = NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter); + int volumeCategoryIndex = NavCategoryStates::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter); IObservableVector ^ categories = unitConverterViewModel->Categories; unitConverterViewModel->CurrentCategory = categories->GetAt(volumeCategoryIndex); unitConverterViewModel->Unit1 = unitConverterViewModel->Units->GetAt(2); diff --git a/src/CalculatorUnitTests/NavCategoryUnitTests.cpp b/src/CalculatorUnitTests/NavCategoryUnitTests.cpp index 637bfb8..912a622 100644 --- a/src/CalculatorUnitTests/NavCategoryUnitTests.cpp +++ b/src/CalculatorUnitTests/NavCategoryUnitTests.cpp @@ -17,136 +17,11 @@ namespace CalculatorUnitTests TEST_CLASS(NavCategoryUnitTests) { public: - TEST_METHOD(Serialize); - TEST_METHOD(Deserialize_AllValid); - TEST_METHOD(Deserialize_AllInvalid); - - TEST_METHOD(IsValidViewMode_AllValid); - TEST_METHOD(IsValidViewMode_AllInvalid); - TEST_METHOD(IsCalculatorViewMode); TEST_METHOD(IsDateCalculatorViewMode); TEST_METHOD(IsConverterViewMode); - - TEST_METHOD(GetFriendlyName); - TEST_METHOD(GetGroupType); - TEST_METHOD(GetIndex); - TEST_METHOD(GetPosition); - TEST_METHOD(GetIndex_GetPosition_Relationship); - TEST_METHOD(GetIndexInGroup); - - TEST_METHOD(GetViewModeForVirtualKey); }; - void NavCategoryUnitTests::Serialize() - { - // While values in other tests may change (for example, the order - // of a navigation item might change), these values should NEVER - // change. We are validating the unique ID for each mode, not - // it's position or index. - VERIFY_ARE_EQUAL(0, NavCategory::Serialize(ViewMode::Standard)); - VERIFY_ARE_EQUAL(1, NavCategory::Serialize(ViewMode::Scientific)); - VERIFY_ARE_EQUAL(2, NavCategory::Serialize(ViewMode::Programmer)); - VERIFY_ARE_EQUAL(3, NavCategory::Serialize(ViewMode::Date)); - VERIFY_ARE_EQUAL(16, NavCategory::Serialize(ViewMode::Currency)); - VERIFY_ARE_EQUAL(4, NavCategory::Serialize(ViewMode::Volume)); - VERIFY_ARE_EQUAL(5, NavCategory::Serialize(ViewMode::Length)); - VERIFY_ARE_EQUAL(6, NavCategory::Serialize(ViewMode::Weight)); - VERIFY_ARE_EQUAL(7, NavCategory::Serialize(ViewMode::Temperature)); - VERIFY_ARE_EQUAL(8, NavCategory::Serialize(ViewMode::Energy)); - VERIFY_ARE_EQUAL(9, NavCategory::Serialize(ViewMode::Area)); - VERIFY_ARE_EQUAL(10, NavCategory::Serialize(ViewMode::Speed)); - VERIFY_ARE_EQUAL(11, NavCategory::Serialize(ViewMode::Time)); - VERIFY_ARE_EQUAL(12, NavCategory::Serialize(ViewMode::Power)); - VERIFY_ARE_EQUAL(13, NavCategory::Serialize(ViewMode::Data)); - VERIFY_ARE_EQUAL(14, NavCategory::Serialize(ViewMode::Pressure)); - VERIFY_ARE_EQUAL(15, NavCategory::Serialize(ViewMode::Angle)); - - VERIFY_ARE_EQUAL(-1, NavCategory::Serialize(ViewMode::None)); - } - - void NavCategoryUnitTests::Deserialize_AllValid() - { - // While values in other tests may change (for example, the order - // of a navigation item might change), these values should NEVER - // change. We are validating the unique ID for each mode, not - // it's position or index. - VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategory::Deserialize(ref new Box(0))); - VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategory::Deserialize(ref new Box(1))); - VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::Deserialize(ref new Box(2))); - VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::Deserialize(ref new Box(3))); - VERIFY_ARE_EQUAL(ViewMode::Currency, NavCategory::Deserialize(ref new Box(16))); - VERIFY_ARE_EQUAL(ViewMode::Volume, NavCategory::Deserialize(ref new Box(4))); - VERIFY_ARE_EQUAL(ViewMode::Length, NavCategory::Deserialize(ref new Box(5))); - VERIFY_ARE_EQUAL(ViewMode::Weight, NavCategory::Deserialize(ref new Box(6))); - VERIFY_ARE_EQUAL(ViewMode::Temperature, NavCategory::Deserialize(ref new Box(7))); - VERIFY_ARE_EQUAL(ViewMode::Energy, NavCategory::Deserialize(ref new Box(8))); - VERIFY_ARE_EQUAL(ViewMode::Area, NavCategory::Deserialize(ref new Box(9))); - VERIFY_ARE_EQUAL(ViewMode::Speed, NavCategory::Deserialize(ref new Box(10))); - VERIFY_ARE_EQUAL(ViewMode::Time, NavCategory::Deserialize(ref new Box(11))); - VERIFY_ARE_EQUAL(ViewMode::Power, NavCategory::Deserialize(ref new Box(12))); - VERIFY_ARE_EQUAL(ViewMode::Data, NavCategory::Deserialize(ref new Box(13))); - VERIFY_ARE_EQUAL(ViewMode::Pressure, NavCategory::Deserialize(ref new Box(14))); - VERIFY_ARE_EQUAL(ViewMode::Angle, NavCategory::Deserialize(ref new Box(15))); - } - - void NavCategoryUnitTests::Deserialize_AllInvalid() - { - VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(nullptr)); - VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new String(L"fail"))); - - // Boundary testing - VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box(-1))); - VERIFY_ARE_EQUAL(ViewMode::None, NavCategory::Deserialize(ref new Box(18))); - } - - void NavCategoryUnitTests::IsValidViewMode_AllValid() - { - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Standard)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Scientific)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Programmer)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Graphing)); - } - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Currency)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Volume)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Length)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Weight)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Temperature)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Energy)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Area)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Speed)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Time)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Power)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Data)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Pressure)); - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(ViewMode::Angle)); - } - - void NavCategoryUnitTests::IsValidViewMode_AllInvalid() - { - VERIFY_IS_FALSE(NavCategory::IsValidViewMode(ViewMode::None)); - - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - // There are 18 total options so int 18 should be the first invalid - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast(17))); - VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast(18))); - } - else - { - // There are 17 total options when graphing calculator is not present, so int 17 should be the first invalid - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast(16))); - VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast(17))); - } - - // Also verify the lower bound - VERIFY_IS_TRUE(NavCategory::IsValidViewMode(static_cast(0))); - VERIFY_IS_FALSE(NavCategory::IsValidViewMode(static_cast(-1))); - } - void NavCategoryUnitTests::IsCalculatorViewMode() { VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Standard)); @@ -154,10 +29,7 @@ namespace CalculatorUnitTests VERIFY_IS_TRUE(NavCategory::IsCalculatorViewMode(ViewMode::Programmer)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Graphing)); - } + VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Graphing)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Currency)); VERIFY_IS_FALSE(NavCategory::IsCalculatorViewMode(ViewMode::Volume)); @@ -181,10 +53,7 @@ namespace CalculatorUnitTests VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Programmer)); VERIFY_IS_TRUE(NavCategory::IsDateCalculatorViewMode(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Graphing)); - } + VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Graphing)); VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Currency)); VERIFY_IS_FALSE(NavCategory::IsDateCalculatorViewMode(ViewMode::Volume)); @@ -207,10 +76,7 @@ namespace CalculatorUnitTests VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Scientific)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Programmer)); VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Graphing)); - } + VERIFY_IS_FALSE(NavCategory::IsConverterViewMode(ViewMode::Graphing)); VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Currency)); VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Volume)); @@ -227,178 +93,45 @@ namespace CalculatorUnitTests VERIFY_IS_TRUE(NavCategory::IsConverterViewMode(ViewMode::Angle)); } - void NavCategoryUnitTests::GetFriendlyName() - { - VERIFY_ARE_EQUAL(StringReference(L"Standard"), NavCategory::GetFriendlyName(ViewMode::Standard)); - VERIFY_ARE_EQUAL(StringReference(L"Scientific"), NavCategory::GetFriendlyName(ViewMode::Scientific)); - VERIFY_ARE_EQUAL(StringReference(L"Programmer"), NavCategory::GetFriendlyName(ViewMode::Programmer)); - VERIFY_ARE_EQUAL(StringReference(L"Date"), NavCategory::GetFriendlyName(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_ARE_EQUAL(StringReference(L"Graphing"), NavCategory::GetFriendlyName(ViewMode::Graphing)); - } - VERIFY_ARE_EQUAL(StringReference(L"Currency"), NavCategory::GetFriendlyName(ViewMode::Currency)); - VERIFY_ARE_EQUAL(StringReference(L"Volume"), NavCategory::GetFriendlyName(ViewMode::Volume)); - VERIFY_ARE_EQUAL(StringReference(L"Length"), NavCategory::GetFriendlyName(ViewMode::Length)); - VERIFY_ARE_EQUAL(StringReference(L"Weight and Mass"), NavCategory::GetFriendlyName(ViewMode::Weight)); - VERIFY_ARE_EQUAL(StringReference(L"Temperature"), NavCategory::GetFriendlyName(ViewMode::Temperature)); - VERIFY_ARE_EQUAL(StringReference(L"Energy"), NavCategory::GetFriendlyName(ViewMode::Energy)); - VERIFY_ARE_EQUAL(StringReference(L"Area"), NavCategory::GetFriendlyName(ViewMode::Area)); - VERIFY_ARE_EQUAL(StringReference(L"Speed"), NavCategory::GetFriendlyName(ViewMode::Speed)); - VERIFY_ARE_EQUAL(StringReference(L"Time"), NavCategory::GetFriendlyName(ViewMode::Time)); - VERIFY_ARE_EQUAL(StringReference(L"Power"), NavCategory::GetFriendlyName(ViewMode::Power)); - VERIFY_ARE_EQUAL(StringReference(L"Data"), NavCategory::GetFriendlyName(ViewMode::Data)); - VERIFY_ARE_EQUAL(StringReference(L"Pressure"), NavCategory::GetFriendlyName(ViewMode::Pressure)); - VERIFY_ARE_EQUAL(StringReference(L"Angle"), NavCategory::GetFriendlyName(ViewMode::Angle)); - - VERIFY_ARE_EQUAL(StringReference(L"None"), NavCategory::GetFriendlyName(ViewMode::None)); - } - - void NavCategoryUnitTests::GetGroupType() - { - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Standard)); - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Scientific)); - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Programmer)); - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Date)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategory::GetGroupType(ViewMode::Graphing)); - } - - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Currency)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Volume)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Length)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Weight)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Temperature)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Energy)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Area)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Speed)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Time)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Power)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Data)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Pressure)); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategory::GetGroupType(ViewMode::Angle)); - } - - void NavCategoryUnitTests::GetIndex() - { - // Index is the 0-based ordering of modes - vector orderedModes; - - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Graphing, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, - ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, - ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle }; - } - else - { - orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, ViewMode::Volume, - ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, ViewMode::Speed, - ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle }; - } - - auto orderedModesSize = size(orderedModes); - for (size_t index = 0; index < orderedModesSize; index++) - { - ViewMode mode = orderedModes[index]; - VERIFY_ARE_EQUAL(index, (size_t)NavCategory::GetIndex(mode)); - } - - VERIFY_ARE_EQUAL(-1, NavCategory::GetIndex(ViewMode::None)); - } - - void NavCategoryUnitTests::GetPosition() - { - // Position is the 1-based ordering of modes - vector orderedModes; - - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Graphing, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, - ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, - ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle }; - } - else - { - orderedModes = { ViewMode::Standard, ViewMode::Scientific, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, ViewMode::Volume, - ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, ViewMode::Speed, - ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle }; - } - - auto orderedModesSize = size(orderedModes); - for (size_t pos = 1; pos <= orderedModesSize; pos++) - { - ViewMode mode = orderedModes[pos - 1]; - VERIFY_ARE_EQUAL(pos, (size_t)NavCategory::GetPosition(mode)); - } - - VERIFY_ARE_EQUAL(-1, NavCategory::GetPosition(ViewMode::None)); - } - - void NavCategoryUnitTests::GetIndex_GetPosition_Relationship() - { - // Index should be 1 less than Position. - // The other checks verify the order of Index and Position. - // Just verify the relationship here. - VERIFY_ARE_EQUAL(NavCategory::GetIndex(ViewMode::Standard) + 1, NavCategory::GetPosition(ViewMode::Standard)); - VERIFY_ARE_EQUAL(NavCategory::GetPosition(ViewMode::Volume) - 1, NavCategory::GetIndex(ViewMode::Volume)); - } - - void NavCategoryUnitTests::GetIndexInGroup() - { - VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Standard, CategoryGroupType::Calculator)); - VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Scientific, CategoryGroupType::Calculator)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Graphing, CategoryGroupType::Calculator)); - VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator)); - VERIFY_ARE_EQUAL(4, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator)); - } - else - { - VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator)); - VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator)); - } - VERIFY_ARE_EQUAL(0, NavCategory::GetIndexInGroup(ViewMode::Currency, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(1, NavCategory::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(2, NavCategory::GetIndexInGroup(ViewMode::Length, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(3, NavCategory::GetIndexInGroup(ViewMode::Weight, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(4, NavCategory::GetIndexInGroup(ViewMode::Temperature, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(5, NavCategory::GetIndexInGroup(ViewMode::Energy, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(6, NavCategory::GetIndexInGroup(ViewMode::Area, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(7, NavCategory::GetIndexInGroup(ViewMode::Speed, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(8, NavCategory::GetIndexInGroup(ViewMode::Time, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(9, NavCategory::GetIndexInGroup(ViewMode::Power, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(10, NavCategory::GetIndexInGroup(ViewMode::Data, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(11, NavCategory::GetIndexInGroup(ViewMode::Pressure, CategoryGroupType::Converter)); - VERIFY_ARE_EQUAL(12, NavCategory::GetIndexInGroup(ViewMode::Angle, CategoryGroupType::Converter)); - - VERIFY_ARE_EQUAL(-1, NavCategory::GetIndexInGroup(ViewMode::None, CategoryGroupType::Calculator)); - VERIFY_ARE_EQUAL(-1, NavCategory::GetIndexInGroup(ViewMode::None, CategoryGroupType::Converter)); - } - - void NavCategoryUnitTests::GetViewModeForVirtualKey() - { - VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number1)); - VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number2)); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) - { - VERIFY_ARE_EQUAL(ViewMode::Graphing, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3)); - VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number4)); - VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number5)); - } - else - { - VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number3)); - VERIFY_ARE_EQUAL(ViewMode::Date, NavCategory::GetViewModeForVirtualKey(MyVirtualKey::Number4)); - } - } - TEST_CLASS(NavCategoryGroupUnitTests) { public: - TEST_METHOD(CreateNavCategoryGroup); + TEST_METHOD(CreateNavCategoryGroup) + { + IObservableVector ^ menuOptions = NavCategoryStates::CreateMenuOptions(); + + VERIFY_ARE_EQUAL(2, menuOptions->Size); + + NavCategoryGroup ^ calculatorGroup = menuOptions->GetAt(0); + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType); + + IObservableVector ^ calculatorCategories = calculatorGroup->Categories; + ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard); + ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific); + ValidateNavCategory(calculatorCategories, 2u, ViewMode::Graphing); + ValidateNavCategory(calculatorCategories, 3u, ViewMode::Programmer); + ValidateNavCategory(calculatorCategories, 4u, ViewMode::Date); + VERIFY_ARE_EQUAL(5, calculatorCategories->Size); + + NavCategoryGroup ^ converterGroup = menuOptions->GetAt(1); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType); + + IObservableVector ^ converterCategories = converterGroup->Categories; + VERIFY_ARE_EQUAL(13, converterCategories->Size); + ValidateNavCategory(converterCategories, 0u, ViewMode::Currency); + ValidateNavCategory(converterCategories, 1u, ViewMode::Volume); + ValidateNavCategory(converterCategories, 2u, ViewMode::Length); + ValidateNavCategory(converterCategories, 3u, ViewMode::Weight); + ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature); + ValidateNavCategory(converterCategories, 5u, ViewMode::Energy); + ValidateNavCategory(converterCategories, 6u, ViewMode::Area); + ValidateNavCategory(converterCategories, 7u, ViewMode::Speed); + ValidateNavCategory(converterCategories, 8u, ViewMode::Time); + ValidateNavCategory(converterCategories, 9u, ViewMode::Power); + ValidateNavCategory(converterCategories, 10u, ViewMode::Data); + ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure); + ValidateNavCategory(converterCategories, 12u, ViewMode::Angle); + } private: void ValidateNavCategory(IObservableVector ^ categories, unsigned int index, ViewMode expectedMode) @@ -407,53 +140,240 @@ namespace CalculatorUnitTests VERIFY_IS_GREATER_THAN(categories->Size, index); NavCategory ^ category = categories->GetAt(index); - VERIFY_ARE_EQUAL(expectedMode, category->Mode); + VERIFY_ARE_EQUAL(expectedMode, category->ViewMode); + VERIFY_ARE_EQUAL(category->IsEnabled, (expectedMode != ViewMode::Graphing) ? true : false); } }; - void NavCategoryGroupUnitTests::CreateNavCategoryGroup() + TEST_CLASS(NavCategoryStatesUnitTests) { - IObservableVector ^ menuOptions = NavCategoryGroup::CreateMenuOptions(); - - VERIFY_ARE_EQUAL(2, menuOptions->Size); - - NavCategoryGroup ^ calculatorGroup = menuOptions->GetAt(0); - VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, calculatorGroup->GroupType); - - IObservableVector ^ calculatorCategories = calculatorGroup->Categories; - ValidateNavCategory(calculatorCategories, 0u, ViewMode::Standard); - ValidateNavCategory(calculatorCategories, 1u, ViewMode::Scientific); - if (Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath")) + public: + TEST_METHOD(Serialize) { - ValidateNavCategory(calculatorCategories, 2u, ViewMode::Graphing); - ValidateNavCategory(calculatorCategories, 3u, ViewMode::Programmer); - ValidateNavCategory(calculatorCategories, 4u, ViewMode::Date); - VERIFY_ARE_EQUAL(5, calculatorCategories->Size); - } - else - { - ValidateNavCategory(calculatorCategories, 2u, ViewMode::Programmer); - ValidateNavCategory(calculatorCategories, 3u, ViewMode::Date); - VERIFY_ARE_EQUAL(4, calculatorCategories->Size); + // While values in other tests may change (for example, the order + // of a navigation item might change), these values should NEVER + // change. We are validating the unique ID for each mode, not + // it's position or index. + VERIFY_ARE_EQUAL(0, NavCategoryStates::Serialize(ViewMode::Standard)); + VERIFY_ARE_EQUAL(1, NavCategoryStates::Serialize(ViewMode::Scientific)); + VERIFY_ARE_EQUAL(2, NavCategoryStates::Serialize(ViewMode::Programmer)); + VERIFY_ARE_EQUAL(3, NavCategoryStates::Serialize(ViewMode::Date)); + VERIFY_ARE_EQUAL(16, NavCategoryStates::Serialize(ViewMode::Currency)); + VERIFY_ARE_EQUAL(4, NavCategoryStates::Serialize(ViewMode::Volume)); + VERIFY_ARE_EQUAL(5, NavCategoryStates::Serialize(ViewMode::Length)); + VERIFY_ARE_EQUAL(6, NavCategoryStates::Serialize(ViewMode::Weight)); + VERIFY_ARE_EQUAL(7, NavCategoryStates::Serialize(ViewMode::Temperature)); + VERIFY_ARE_EQUAL(8, NavCategoryStates::Serialize(ViewMode::Energy)); + VERIFY_ARE_EQUAL(9, NavCategoryStates::Serialize(ViewMode::Area)); + VERIFY_ARE_EQUAL(10, NavCategoryStates::Serialize(ViewMode::Speed)); + VERIFY_ARE_EQUAL(11, NavCategoryStates::Serialize(ViewMode::Time)); + VERIFY_ARE_EQUAL(12, NavCategoryStates::Serialize(ViewMode::Power)); + VERIFY_ARE_EQUAL(13, NavCategoryStates::Serialize(ViewMode::Data)); + VERIFY_ARE_EQUAL(14, NavCategoryStates::Serialize(ViewMode::Pressure)); + VERIFY_ARE_EQUAL(15, NavCategoryStates::Serialize(ViewMode::Angle)); + + VERIFY_ARE_EQUAL(-1, NavCategoryStates::Serialize(ViewMode::None)); } - NavCategoryGroup ^ converterGroup = menuOptions->GetAt(1); - VERIFY_ARE_EQUAL(CategoryGroupType::Converter, converterGroup->GroupType); + TEST_METHOD(Deserialize_AllValid) + { + // While values in other tests may change (for example, the order + // of a navigation item might change), these values should NEVER + // change. We are validating the unique ID for each mode, not + // it's position or index. + VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategoryStates::Deserialize(ref new Box(0))); + VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategoryStates::Deserialize(ref new Box(1))); + VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategoryStates::Deserialize(ref new Box(2))); + VERIFY_ARE_EQUAL(ViewMode::Date, NavCategoryStates::Deserialize(ref new Box(3))); + VERIFY_ARE_EQUAL(ViewMode::Currency, NavCategoryStates::Deserialize(ref new Box(16))); + VERIFY_ARE_EQUAL(ViewMode::Volume, NavCategoryStates::Deserialize(ref new Box(4))); + VERIFY_ARE_EQUAL(ViewMode::Length, NavCategoryStates::Deserialize(ref new Box(5))); + VERIFY_ARE_EQUAL(ViewMode::Weight, NavCategoryStates::Deserialize(ref new Box(6))); + VERIFY_ARE_EQUAL(ViewMode::Temperature, NavCategoryStates::Deserialize(ref new Box(7))); + VERIFY_ARE_EQUAL(ViewMode::Energy, NavCategoryStates::Deserialize(ref new Box(8))); + VERIFY_ARE_EQUAL(ViewMode::Area, NavCategoryStates::Deserialize(ref new Box(9))); + VERIFY_ARE_EQUAL(ViewMode::Speed, NavCategoryStates::Deserialize(ref new Box(10))); + VERIFY_ARE_EQUAL(ViewMode::Time, NavCategoryStates::Deserialize(ref new Box(11))); + VERIFY_ARE_EQUAL(ViewMode::Power, NavCategoryStates::Deserialize(ref new Box(12))); + VERIFY_ARE_EQUAL(ViewMode::Data, NavCategoryStates::Deserialize(ref new Box(13))); + VERIFY_ARE_EQUAL(ViewMode::Pressure, NavCategoryStates::Deserialize(ref new Box(14))); + VERIFY_ARE_EQUAL(ViewMode::Angle, NavCategoryStates::Deserialize(ref new Box(15))); + } - IObservableVector ^ converterCategories = converterGroup->Categories; - VERIFY_ARE_EQUAL(13, converterCategories->Size); - ValidateNavCategory(converterCategories, 0u, ViewMode::Currency); - ValidateNavCategory(converterCategories, 1u, ViewMode::Volume); - ValidateNavCategory(converterCategories, 2u, ViewMode::Length); - ValidateNavCategory(converterCategories, 3u, ViewMode::Weight); - ValidateNavCategory(converterCategories, 4u, ViewMode::Temperature); - ValidateNavCategory(converterCategories, 5u, ViewMode::Energy); - ValidateNavCategory(converterCategories, 6u, ViewMode::Area); - ValidateNavCategory(converterCategories, 7u, ViewMode::Speed); - ValidateNavCategory(converterCategories, 8u, ViewMode::Time); - ValidateNavCategory(converterCategories, 9u, ViewMode::Power); - ValidateNavCategory(converterCategories, 10u, ViewMode::Data); - ValidateNavCategory(converterCategories, 11u, ViewMode::Pressure); - ValidateNavCategory(converterCategories, 12u, ViewMode::Angle); - } + TEST_METHOD(Deserialize_AllInvalid) + { + VERIFY_ARE_EQUAL(ViewMode::None, NavCategoryStates::Deserialize(nullptr)); + VERIFY_ARE_EQUAL(ViewMode::None, NavCategoryStates::Deserialize(ref new String(L"fail"))); + + // Boundary testing + VERIFY_ARE_EQUAL(ViewMode::None, NavCategoryStates::Deserialize(ref new Box(-1))); + VERIFY_ARE_EQUAL(ViewMode::None, NavCategoryStates::Deserialize(ref new Box(18))); + } + + TEST_METHOD(IsValidViewMode_AllValid) + { + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Standard)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Scientific)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Programmer)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Date)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Graphing)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Currency)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Volume)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Length)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Weight)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Temperature)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Energy)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Area)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Speed)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Time)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Power)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Data)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Pressure)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(ViewMode::Angle)); + } + + TEST_METHOD(IsValidViewMode_AllInvalid) + { + VERIFY_IS_FALSE(NavCategoryStates::IsValidViewMode(ViewMode::None)); + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(static_cast(17))); + VERIFY_IS_FALSE(NavCategoryStates::IsValidViewMode(static_cast(18))); + + // Also verify the lower bound + VERIFY_IS_TRUE(NavCategoryStates::IsValidViewMode(static_cast(0))); + VERIFY_IS_FALSE(NavCategoryStates::IsValidViewMode(static_cast(-1))); + } + + TEST_METHOD(GetFriendlyName) + { + VERIFY_ARE_EQUAL(StringReference(L"Standard"), NavCategoryStates::GetFriendlyName(ViewMode::Standard)); + VERIFY_ARE_EQUAL(StringReference(L"Scientific"), NavCategoryStates::GetFriendlyName(ViewMode::Scientific)); + VERIFY_ARE_EQUAL(StringReference(L"Programmer"), NavCategoryStates::GetFriendlyName(ViewMode::Programmer)); + VERIFY_ARE_EQUAL(StringReference(L"Date"), NavCategoryStates::GetFriendlyName(ViewMode::Date)); + VERIFY_ARE_EQUAL(StringReference(L"Graphing"), NavCategoryStates::GetFriendlyName(ViewMode::Graphing)); + VERIFY_ARE_EQUAL(StringReference(L"Currency"), NavCategoryStates::GetFriendlyName(ViewMode::Currency)); + VERIFY_ARE_EQUAL(StringReference(L"Volume"), NavCategoryStates::GetFriendlyName(ViewMode::Volume)); + VERIFY_ARE_EQUAL(StringReference(L"Length"), NavCategoryStates::GetFriendlyName(ViewMode::Length)); + VERIFY_ARE_EQUAL(StringReference(L"Weight and Mass"), NavCategoryStates::GetFriendlyName(ViewMode::Weight)); + VERIFY_ARE_EQUAL(StringReference(L"Temperature"), NavCategoryStates::GetFriendlyName(ViewMode::Temperature)); + VERIFY_ARE_EQUAL(StringReference(L"Energy"), NavCategoryStates::GetFriendlyName(ViewMode::Energy)); + VERIFY_ARE_EQUAL(StringReference(L"Area"), NavCategoryStates::GetFriendlyName(ViewMode::Area)); + VERIFY_ARE_EQUAL(StringReference(L"Speed"), NavCategoryStates::GetFriendlyName(ViewMode::Speed)); + VERIFY_ARE_EQUAL(StringReference(L"Time"), NavCategoryStates::GetFriendlyName(ViewMode::Time)); + VERIFY_ARE_EQUAL(StringReference(L"Power"), NavCategoryStates::GetFriendlyName(ViewMode::Power)); + VERIFY_ARE_EQUAL(StringReference(L"Data"), NavCategoryStates::GetFriendlyName(ViewMode::Data)); + VERIFY_ARE_EQUAL(StringReference(L"Pressure"), NavCategoryStates::GetFriendlyName(ViewMode::Pressure)); + VERIFY_ARE_EQUAL(StringReference(L"Angle"), NavCategoryStates::GetFriendlyName(ViewMode::Angle)); + + VERIFY_ARE_EQUAL(StringReference(L"None"), NavCategoryStates::GetFriendlyName(ViewMode::None)); + } + + TEST_METHOD(GetGroupType) + { + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategoryStates::GetGroupType(ViewMode::Standard)); + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategoryStates::GetGroupType(ViewMode::Scientific)); + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategoryStates::GetGroupType(ViewMode::Programmer)); + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategoryStates::GetGroupType(ViewMode::Date)); + VERIFY_ARE_EQUAL(CategoryGroupType::Calculator, NavCategoryStates::GetGroupType(ViewMode::Graphing)); + + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Currency)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Volume)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Length)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Weight)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Temperature)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Energy)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Area)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Speed)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Time)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Power)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Data)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Pressure)); + VERIFY_ARE_EQUAL(CategoryGroupType::Converter, NavCategoryStates::GetGroupType(ViewMode::Angle)); + } + + TEST_METHOD(GetIndex) + { + // Index is the 0-based ordering of modes + const auto orderedModesSize = _orderedModes.size(); + for (size_t index = 0; index < orderedModesSize; index++) + { + ViewMode mode = _orderedModes[index]; + VERIFY_ARE_EQUAL(index, (size_t)NavCategoryStates::GetIndex(mode)); + } + + VERIFY_ARE_EQUAL(-1, NavCategoryStates::GetIndex(ViewMode::None)); + } + + TEST_METHOD(GetPosition) + { + // Position is the 1-based ordering of modes + const auto orderedModesSize = _orderedModes.size(); + for (size_t pos = 1; pos <= orderedModesSize; pos++) + { + ViewMode mode = _orderedModes[pos - 1]; + VERIFY_ARE_EQUAL(pos, (size_t)NavCategoryStates::GetPosition(mode)); + } + + VERIFY_ARE_EQUAL(-1, NavCategoryStates::GetPosition(ViewMode::None)); + } + + TEST_METHOD(GetIndex_GetPosition_Relationship) + { + // Index should be 1 less than Position. + // The other checks verify the order of Index and Position. + // Just verify the relationship here. + VERIFY_ARE_EQUAL(NavCategoryStates::GetIndex(ViewMode::Standard) + 1, NavCategoryStates::GetPosition(ViewMode::Standard)); + VERIFY_ARE_EQUAL(NavCategoryStates::GetPosition(ViewMode::Volume) - 1, NavCategoryStates::GetIndex(ViewMode::Volume)); + } + + TEST_METHOD(GetIndexInGroup) + { + VERIFY_ARE_EQUAL(0, NavCategoryStates::GetIndexInGroup(ViewMode::Standard, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(1, NavCategoryStates::GetIndexInGroup(ViewMode::Scientific, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(2, NavCategoryStates::GetIndexInGroup(ViewMode::Graphing, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(3, NavCategoryStates::GetIndexInGroup(ViewMode::Programmer, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(4, NavCategoryStates::GetIndexInGroup(ViewMode::Date, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(0, NavCategoryStates::GetIndexInGroup(ViewMode::Currency, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(1, NavCategoryStates::GetIndexInGroup(ViewMode::Volume, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(2, NavCategoryStates::GetIndexInGroup(ViewMode::Length, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(3, NavCategoryStates::GetIndexInGroup(ViewMode::Weight, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(4, NavCategoryStates::GetIndexInGroup(ViewMode::Temperature, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(5, NavCategoryStates::GetIndexInGroup(ViewMode::Energy, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(6, NavCategoryStates::GetIndexInGroup(ViewMode::Area, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(7, NavCategoryStates::GetIndexInGroup(ViewMode::Speed, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(8, NavCategoryStates::GetIndexInGroup(ViewMode::Time, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(9, NavCategoryStates::GetIndexInGroup(ViewMode::Power, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(10, NavCategoryStates::GetIndexInGroup(ViewMode::Data, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(11, NavCategoryStates::GetIndexInGroup(ViewMode::Pressure, CategoryGroupType::Converter)); + VERIFY_ARE_EQUAL(12, NavCategoryStates::GetIndexInGroup(ViewMode::Angle, CategoryGroupType::Converter)); + + VERIFY_ARE_EQUAL(-1, NavCategoryStates::GetIndexInGroup(ViewMode::None, CategoryGroupType::Calculator)); + VERIFY_ARE_EQUAL(-1, NavCategoryStates::GetIndexInGroup(ViewMode::None, CategoryGroupType::Converter)); + } + + TEST_METHOD(GetViewModeForVirtualKey) + { + VERIFY_ARE_EQUAL(ViewMode::Standard, NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey::Number1)); + VERIFY_ARE_EQUAL(ViewMode::Scientific, NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey::Number2)); + VERIFY_ARE_EQUAL(ViewMode::Graphing, NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey::Number3)); + VERIFY_ARE_EQUAL(ViewMode::Programmer, NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey::Number4)); + VERIFY_ARE_EQUAL(ViewMode::Date, NavCategoryStates::GetViewModeForVirtualKey(MyVirtualKey::Number5)); + } + + TEST_METHOD(IsViewModeEnabled_ShouldBeTrue_ExceptGraphingMode) + { + for (const auto& mode : _orderedModes) + { + if (mode != ViewMode::Graphing) + { + VERIFY_IS_TRUE(NavCategoryStates::IsViewModeEnabled(mode)); + } + } + } + + private: + const static inline std::vector _orderedModes { + ViewMode::Standard, ViewMode::Scientific, ViewMode::Graphing, ViewMode::Programmer, ViewMode::Date, ViewMode::Currency, + ViewMode::Volume, ViewMode::Length, ViewMode::Weight, ViewMode::Temperature, ViewMode::Energy, ViewMode::Area, + ViewMode::Speed, ViewMode::Time, ViewMode::Power, ViewMode::Data, ViewMode::Pressure, ViewMode::Angle }; + }; }