Hide or Disable graphing calculator menu item when necessary (#848)

This commit is contained in:
Rudy Huyn
2019-12-06 15:03:07 -08:00
committed by GitHub
parent 81747a0a93
commit 503831895d
8 changed files with 369 additions and 297 deletions

View File

@@ -5,6 +5,7 @@
#include "NavCategory.h"
#include "AppResourceProvider.h"
#include "Common/LocalizationStringUtil.h"
#include <initializer_list>
using namespace CalculatorApp;
using namespace CalculatorApp::Common;
@@ -22,12 +23,6 @@ static constexpr bool SUPPORTS_ALL = true;
static constexpr bool SUPPORTS_NEGATIVE = true;
static constexpr bool POSITIVE_ONLY = false;
// The order of items in this list determines the order of groups in the menu.
static constexpr array<const NavCategoryGroupInitializer, 2> s_categoryGroupManifest = {
NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" },
NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" }
};
// vvv THESE CONSTANTS SHOULD NEVER CHANGE vvv
static constexpr int STANDARD_ID = 0;
static constexpr int SCIENTIFIC_ID = 1;
@@ -49,151 +44,263 @@ static constexpr int CURRENCY_ID = 16;
static constexpr int GRAPHING_ID = 17;
// ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^
wchar_t* towchar_t(int number)
{
auto wstr = to_wstring(number);
return _wcsdup(wstr.c_str());
}
extern "C"
{
WINADVAPI LSTATUS APIENTRY RegGetValueW(
_In_ HKEY hkey,
_In_opt_ LPCWSTR lpSubKey,
_In_opt_ LPCWSTR lpValue,
_In_ DWORD dwFlags,
_Out_opt_ LPDWORD pdwType,
_When_(
(dwFlags & 0x7F) == RRF_RT_REG_SZ || (dwFlags & 0x7F) == RRF_RT_REG_EXPAND_SZ || (dwFlags & 0x7F) == (RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ)
|| *pdwType == REG_SZ || *pdwType == REG_EXPAND_SZ,
_Post_z_) _When_((dwFlags & 0x7F) == RRF_RT_REG_MULTI_SZ || *pdwType == REG_MULTI_SZ, _Post_ _NullNull_terminated_)
_Out_writes_bytes_to_opt_(*pcbData, *pcbData) PVOID pvData,
_Inout_opt_ LPDWORD pcbData);
}
bool IsGraphingModeAvailable()
{
static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath");
return supportGraph;
}
Box<bool> ^ _isGraphingModeEnabledCached = nullptr;
bool IsGraphingModeEnabled()
{
if (!IsGraphingModeAvailable())
{
return false;
}
if (_isGraphingModeEnabledCached != nullptr)
{
return _isGraphingModeEnabledCached->Value;
}
DWORD allowGraphingCalculator{ 0 };
DWORD bufferSize{ sizeof(allowGraphingCalculator) };
// Make sure to call RegGetValueW only on Windows 10 1903+
if (RegGetValueW(
HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Calculator",
L"AllowGraphingCalculator",
RRF_RT_REG_DWORD | RRF_RT_REG_BINARY,
nullptr,
reinterpret_cast<LPBYTE>(&allowGraphingCalculator),
&bufferSize)
== ERROR_SUCCESS)
{
_isGraphingModeEnabledCached = allowGraphingCalculator != 0;
}
else
{
_isGraphingModeEnabledCached = true;
}
return _isGraphingModeEnabledCached->Value;
}
// The order of items in this list determines the order of items in the menu.
static constexpr array<const NavCategoryInitializer, 18> s_categoryManifest = { NavCategoryInitializer{ ViewMode::Standard,
STANDARD_ID,
L"Standard",
L"StandardMode",
L"\uE8EF",
CategoryGroupType::Calculator,
MyVirtualKey::Number1,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Scientific,
SCIENTIFIC_ID,
L"Scientific",
L"ScientificMode",
L"\uF196",
CategoryGroupType::Calculator,
MyVirtualKey::Number2,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Programmer,
PROGRAMMER_ID,
L"Programmer",
L"ProgrammerMode",
L"\uECCE",
CategoryGroupType::Calculator,
MyVirtualKey::Number3,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Graphing,
GRAPHING_ID,
L"Graphing",
L"GraphingCalculatorMode",
L"\uF770",
CategoryGroupType::Calculator,
MyVirtualKey::Number5,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Date,
DATE_ID,
L"Date",
L"DateCalculationMode",
L"\uE787",
CategoryGroupType::Calculator,
MyVirtualKey::Number4,
SUPPORTS_ALL },
NavCategoryInitializer{ ViewMode::Currency,
CURRENCY_ID,
L"Currency",
L"CategoryName_Currency",
L"\uEB0D",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Volume,
VOLUME_ID,
L"Volume",
L"CategoryName_Volume",
L"\uF1AA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Length,
LENGTH_ID,
L"Length",
L"CategoryName_Length",
L"\uECC6",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Weight,
WEIGHT_ID,
L"Weight and Mass",
L"CategoryName_Weight",
L"\uF4C1",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Temperature,
TEMPERATURE_ID,
L"Temperature",
L"CategoryName_Temperature",
L"\uE7A3",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
NavCategoryInitializer{ ViewMode::Energy,
ENERGY_ID,
L"Energy",
L"CategoryName_Energy",
L"\uECAD",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Area,
AREA_ID,
L"Area",
L"CategoryName_Area",
L"\uE809",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Speed,
SPEED_ID,
L"Speed",
L"CategoryName_Speed",
L"\uEADA",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Time,
TIME_ID,
L"Time",
L"CategoryName_Time",
L"\uE917",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Power,
POWER_ID,
L"Power",
L"CategoryName_Power",
L"\uE945",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE },
NavCategoryInitializer{ ViewMode::Data,
DATA_ID,
L"Data",
L"CategoryName_Data",
L"\uF20F",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Pressure,
PRESSURE_ID,
L"Pressure",
L"CategoryName_Pressure",
L"\uEC4A",
CategoryGroupType::Converter,
MyVirtualKey::None,
POSITIVE_ONLY },
NavCategoryInitializer{ ViewMode::Angle,
ANGLE_ID,
L"Angle",
L"CategoryName_Angle",
L"\uF515",
CategoryGroupType::Converter,
MyVirtualKey::None,
SUPPORTS_NEGATIVE } };
static const list<NavCategoryInitializer> s_categoryManifest = [] {
auto res = list<NavCategoryInitializer>{ 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)
{
const 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;
}();
// This function should only be used when storing the mode to app data.
int NavCategory::Serialize(ViewMode mode)
@@ -220,6 +327,14 @@ ViewMode NavCategory::Deserialize(Platform::Object ^ obj)
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;
}
}
@@ -402,10 +517,12 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti
categoryName,
categoryAutomationName,
StringReference(categoryInitializer.glyph),
resProvider->GetResourceString(nameResourceKey + "AccessKey"),
categoryInitializer.accessKey != nullptr ? ref new String(categoryInitializer.accessKey)
: resProvider->GetResourceString(nameResourceKey + "AccessKey"),
groupMode,
categoryInitializer.viewMode,
categoryInitializer.supportsNegative));
categoryInitializer.supportsNegative,
categoryInitializer.isEnabled));
}
}
}
@@ -420,29 +537,12 @@ IObservableVector<NavCategoryGroup ^> ^ NavCategoryGroup::CreateMenuOptions()
NavCategoryGroup ^ NavCategoryGroup::CreateCalculatorCategory()
{
return ref new NavCategoryGroup(s_categoryGroupManifest.at(0));
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" });
}
NavCategoryGroup ^ NavCategoryGroup::CreateConverterCategory()
{
return ref new NavCategoryGroup(s_categoryGroupManifest.at(1));
}
vector<NavCategoryInitializer> NavCategoryGroup::GetInitializerCategoryGroup(CategoryGroupType groupType)
{
vector<NavCategoryInitializer> initializers{};
copy_if(begin(s_categoryManifest), end(s_categoryManifest), back_inserter(initializers), [groupType](const NavCategoryInitializer& initializer) {
return initializer.groupType == groupType;
});
return initializers;
}
String ^ NavCategoryGroup::GetHeaderResourceKey(CategoryGroupType type)
{
auto iter = find_if(begin(s_categoryGroupManifest), end(s_categoryGroupManifest), [type](const NavCategoryGroupInitializer& initializer) {
return initializer.type == type;
});
return (iter != s_categoryGroupManifest.end()) ? StringReference(iter->headerResourceKey) : nullptr;
return ref new NavCategoryGroup(
NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" });
}

View File

@@ -67,7 +67,9 @@ namespace CalculatorApp
wchar_t const* glyph,
CategoryGroupType group,
MyVirtualKey vKey,
bool categorySupportsNegative)
wchar_t const* aKey,
bool categorySupportsNegative,
bool enabled)
: viewMode(mode)
, serializationId(id)
, friendlyName(name)
@@ -75,7 +77,9 @@ namespace CalculatorApp
, glyph(glyph)
, groupType(group)
, virtualKey(vKey)
, accessKey(aKey)
, supportsNegative(categorySupportsNegative)
, isEnabled(enabled)
{
}
@@ -86,7 +90,9 @@ namespace CalculatorApp
const wchar_t* const glyph;
const CategoryGroupType groupType;
const MyVirtualKey virtualKey;
const wchar_t* const accessKey;
const bool supportsNegative;
const bool isEnabled;
};
private
@@ -110,45 +116,17 @@ namespace CalculatorApp
{
public:
OBSERVABLE_OBJECT();
PROPERTY_R(Platform::String ^, Name);
PROPERTY_R(Platform::String ^, AutomationName);
PROPERTY_R(Platform::String ^, Glyph);
PROPERTY_R(ViewMode, Mode);
PROPERTY_R(Platform::String ^, AccessKey);
PROPERTY_R(bool, SupportsNegative);
PROPERTY_R(bool, IsEnabled);
property Platform::String
^ Name { Platform::String ^ get() { return m_name; } }
^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } }
property Platform::String
^ AutomationName { Platform::String ^ get() { return m_automationName; } }
property Platform::String
^ Glyph { Platform::String ^ get() { return m_glyph; } }
property int Position
{
int get()
{
return m_position;
}
}
property ViewMode Mode
{
ViewMode get()
{
return m_viewMode;
}
}
property Platform::String
^ AutomationId { Platform::String ^ get() { return m_viewMode.ToString(); } }
property Platform::String
^ AccessKey { Platform::String ^ get() { return m_accessKey; } }
property bool SupportsNegative
{
bool get()
{
return m_supportsNegative;
}
}
// For saving/restoring last mode used.
static int Serialize(ViewMode mode);
@@ -180,16 +158,17 @@ namespace CalculatorApp
Platform::String ^ accessKey,
Platform::String ^ mode,
ViewMode viewMode,
bool supportsNegative)
: m_name(name)
, m_automationName(automationName)
, m_glyph(glyph)
, m_accessKey(accessKey)
, m_mode(mode)
, m_viewMode(viewMode)
, m_supportsNegative(supportsNegative)
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)
{
m_position = NavCategory::GetPosition(m_viewMode);
}
static std::vector<MyVirtualKey> GetCategoryAcceleratorKeys();
@@ -197,14 +176,7 @@ namespace CalculatorApp
private:
static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType);
ViewMode m_viewMode;
Platform::String ^ m_name;
Platform::String ^ m_automationName;
Platform::String ^ m_glyph;
Platform::String ^ m_accessKey;
Platform::String ^ m_mode;
int m_position;
bool m_supportsNegative;
Platform::String ^ m_modeString;
};
[Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
@@ -218,15 +190,11 @@ namespace CalculatorApp
static Windows::Foundation::Collections::IObservableVector<NavCategoryGroup ^> ^ CreateMenuOptions();
static Platform::String ^ GetHeaderResourceKey(CategoryGroupType type);
internal : static NavCategoryGroup ^ CreateCalculatorCategory();
static NavCategoryGroup ^ CreateConverterCategory();
private:
NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer);
static std::vector<NavCategoryInitializer> GetInitializerCategoryGroup(CategoryGroupType groupType);
};
}
}

View File

@@ -40,6 +40,7 @@
#include "winrt/Windows.Globalization.DateTimeFormatting.h"
#include "winrt/Windows.System.UserProfile.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.Foundation.Metadata.h"
// The following namespaces exist as a convenience to resolve
// ambiguity for Windows types in the Windows::UI::Xaml::Automation::Peers