mirror of
https://github.com/Microsoft/calculator.git
synced 2025-07-16 02:02:51 -07:00
Merge remote-tracking branch 'upstream/feature/GraphingCalculator' into mergeFeature
This commit is contained in:
commit
d2b2853114
129 changed files with 15906 additions and 928 deletions
|
@ -43,6 +43,7 @@ namespace
|
|||
ApplicationViewModel::ApplicationViewModel()
|
||||
: m_CalculatorViewModel(nullptr)
|
||||
, m_DateCalcViewModel(nullptr)
|
||||
, m_GraphingCalcViewModel(nullptr)
|
||||
, m_ConverterViewModel(nullptr)
|
||||
, m_PreviousMode(ViewMode::None)
|
||||
, m_mode(ViewMode::None)
|
||||
|
@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged()
|
|||
}
|
||||
m_CalculatorViewModel->SetCalculatorType(m_mode);
|
||||
}
|
||||
else if (NavCategory::IsGraphingCalculatorViewMode(m_mode))
|
||||
{
|
||||
if (!m_GraphingCalcViewModel)
|
||||
{
|
||||
m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel();
|
||||
}
|
||||
}
|
||||
else if (NavCategory::IsDateCalculatorViewMode(m_mode))
|
||||
{
|
||||
if (!m_DateCalcViewModel)
|
||||
|
@ -182,7 +190,7 @@ void ApplicationViewModel::OnCopyCommand(Object ^ parameter)
|
|||
{
|
||||
DateCalcViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
else
|
||||
else if (NavCategory::IsCalculatorViewMode(m_mode))
|
||||
{
|
||||
CalculatorViewModel->OnCopyCommand(parameter);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "StandardCalculatorViewModel.h"
|
||||
#include "DateCalculatorViewModel.h"
|
||||
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
|
||||
#include "UnitConverterViewModel.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
|
@ -21,6 +22,7 @@ namespace CalculatorApp
|
|||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel ^, DateCalcViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(GraphingCalculatorViewModel ^, GraphingCalcViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(UnitConverterViewModel ^, ConverterViewModel);
|
||||
OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode);
|
||||
OBSERVABLE_PROPERTY_R(bool, IsAlwaysOnTop);
|
||||
|
|
|
@ -122,29 +122,9 @@
|
|||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PropertyGroup>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<GenerateProjectSpecificOutputFolder>true</GenerateProjectSpecificOutputFolder>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
|
@ -348,6 +328,11 @@
|
|||
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
|
||||
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
|
||||
<ClInclude Include="DateCalculatorViewModel.h" />
|
||||
<ClInclude Include="GraphingCalculatorEnums.h" />
|
||||
<ClInclude Include="GraphingCalculator\EquationViewModel.h" />
|
||||
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h" />
|
||||
<ClInclude Include="GraphingCalculator\VariableViewModel.h" />
|
||||
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h" />
|
||||
<ClInclude Include="HistoryItemViewModel.h" />
|
||||
<ClInclude Include="HistoryViewModel.h" />
|
||||
<ClInclude Include="MemoryItemViewModel.h" />
|
||||
|
@ -380,6 +365,9 @@
|
|||
<ClCompile Include="DataLoaders\CurrencyHttpClient.cpp" />
|
||||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp" />
|
||||
<ClCompile Include="DateCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp" />
|
||||
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp" />
|
||||
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp" />
|
||||
<ClCompile Include="HistoryItemViewModel.cpp" />
|
||||
<ClCompile Include="HistoryViewModel.cpp" />
|
||||
<ClCompile Include="MemoryItemViewModel.cpp" />
|
||||
|
@ -400,6 +388,9 @@
|
|||
<ProjectReference Include="..\CalcManager\CalcManager.vcxproj">
|
||||
<Project>{311e866d-8b93-4609-a691-265941fee101}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GraphControl\GraphControl.vcxproj">
|
||||
<Project>{e727a92b-f149-492c-8117-c039a298719b}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup Condition="!Exists('DataLoaders\DataLoaderConstants.h')">
|
||||
<ClCompile>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<Filter Include="DataLoaders">
|
||||
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="GraphingCalculator">
|
||||
<UniqueIdentifier>{cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp" />
|
||||
|
@ -80,9 +83,18 @@
|
|||
<ClCompile Include="DataLoaders\UnitConverterDataLoader.cpp">
|
||||
<Filter>DataLoaders</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GraphingCalculator\EquationViewModel.cpp">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GraphingCalculator\GraphingCalculatorViewModel.cpp">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Common\Automation\NarratorAnnouncement.cpp">
|
||||
<Filter>Common\Automation</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GraphingCalculator\GraphingSettingsViewModel.cpp">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
|
@ -199,6 +211,21 @@
|
|||
<ClInclude Include="Common\NumberBase.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GraphingCalculator\EquationViewModel.h">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GraphingCalculatorEnums.h">
|
||||
<Filter>Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GraphingCalculator\VariableViewModel.h">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GraphingCalculator\GraphingSettingsViewModel.h">
|
||||
<Filter>GraphingCalculator</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="DataLoaders\DefaultFromToCurrency.json">
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "NarratorAnnouncement.h"
|
||||
|
||||
// Declaration of the INarratorAnnouncementHost interface.
|
||||
// This interface exists to hide the concrete announcement host
|
||||
// being used. Depending on the version of the OS the app is running on,
|
||||
// the app may need a host that uses LiveRegionChanged or RaiseNotification.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public
|
||||
interface class INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
// Is the host available on this OS.
|
||||
bool IsHostAvailable();
|
||||
|
||||
// Make a new instance of a concrete host.
|
||||
INarratorAnnouncementHost ^ MakeHost();
|
||||
|
||||
// Make an announcement using the concrete host's preferred method.
|
||||
void Announce(NarratorAnnouncement ^ announcement);
|
||||
};
|
||||
}
|
42
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
42
src/CalcViewModel/Common/Automation/LiveRegionHost.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
LiveRegionHost::LiveRegionHost()
|
||||
: m_host(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool LiveRegionHost::IsHostAvailable()
|
||||
{
|
||||
// LiveRegion is always available.
|
||||
return true;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost ^ LiveRegionHost::MakeHost()
|
||||
{
|
||||
return ref new LiveRegionHost();
|
||||
}
|
||||
|
||||
void LiveRegionHost::Announce(NarratorAnnouncement ^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
AutomationProperties::SetLiveSetting(m_host, AutomationLiveSetting::Assertive);
|
||||
}
|
||||
|
||||
AutomationProperties::SetName(m_host, announcement->Announcement);
|
||||
AutomationPeer ^ peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged);
|
||||
}
|
||||
}
|
33
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
33
src/CalcViewModel/Common/Automation/LiveRegionHost.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the LiveRegionHost class.
|
||||
// This class announces NarratorAnnouncements using the LiveRegionChanged event.
|
||||
// This event is unreliable and should be deprecated in favor of the new
|
||||
// RaiseNotification API in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
// This class exists so that the app can run on RS2 and use LiveRegions
|
||||
// to host notifications on those builds.
|
||||
// When the app switches to min version RS3, this class can be removed
|
||||
// and the app will switch to using the Notification API.
|
||||
// TODO - MSFT 12735088
|
||||
public
|
||||
ref class LiveRegionHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
LiveRegionHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost ^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement ^ announcement);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement ^ m_host;
|
||||
};
|
||||
}
|
|
@ -23,6 +23,7 @@ namespace CalculatorApp::Common::Automation
|
|||
StringReference DisplayCopied(L"DisplayCopied");
|
||||
StringReference OpenParenthesisCountChanged(L"OpenParenthesisCountChanged");
|
||||
StringReference NoParenthesisAdded(L"NoParenthesisAdded");
|
||||
StringReference GraphModeChanged(L"GraphModeChanged");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,3 +141,12 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnounc
|
|||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
||||
NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphModeChangedAnnouncement(Platform::String ^ announcement)
|
||||
{
|
||||
return ref new NarratorAnnouncement(
|
||||
announcement,
|
||||
CalculatorActivityIds::GraphModeChanged,
|
||||
AutomationNotificationKind::ActionCompleted,
|
||||
AutomationNotificationProcessing::ImportantMostRecent);
|
||||
}
|
||||
|
|
|
@ -66,5 +66,8 @@ public
|
|||
|
||||
static NarratorAnnouncement ^ GetOpenParenthesisCountChangedAnnouncement(Platform::String ^ announcement);
|
||||
static NarratorAnnouncement ^ GetNoRightParenthesisAddedAnnouncement(Platform::String ^ announcement);
|
||||
|
||||
static NarratorAnnouncement ^ GetGraphModeChangedAnnouncement(Platform::String ^ announcement);
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NarratorAnnouncementHostFactory.h"
|
||||
#include "NotificationHost.h"
|
||||
#include "LiveRegionHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace std;
|
||||
|
||||
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::s_hostProducer;
|
||||
vector<INarratorAnnouncementHost ^> NarratorAnnouncementHostFactory::s_hosts;
|
||||
|
||||
// This static variable is used only to call the initialization function, to initialize the other static variables.
|
||||
int NarratorAnnouncementHostFactory::s_init = NarratorAnnouncementHostFactory::Initialize();
|
||||
int NarratorAnnouncementHostFactory::Initialize()
|
||||
{
|
||||
RegisterHosts();
|
||||
NarratorAnnouncementHostFactory::s_hostProducer = GetHostProducer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// For now, there are two type of announcement hosts.
|
||||
// We'd prefer to use Notification if it's available and fall back to LiveRegion
|
||||
// if not. The availability of the host depends on the version of the OS the app is running on.
|
||||
// When the app switches to min version RS3, the LiveRegionHost can be removed and we will always
|
||||
// use NotificationHost.
|
||||
// TODO - MSFT 12735088
|
||||
void NarratorAnnouncementHostFactory::RegisterHosts()
|
||||
{
|
||||
// The host that will be used is the first available host,
|
||||
// therefore, order of hosts is important here.
|
||||
NarratorAnnouncementHostFactory::s_hosts = { ref new NotificationHost(), ref new LiveRegionHost() };
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::GetHostProducer()
|
||||
{
|
||||
for (INarratorAnnouncementHost ^ host : NarratorAnnouncementHostFactory::s_hosts)
|
||||
{
|
||||
if (host->IsHostAvailable())
|
||||
{
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
assert(false && L"No suitable AnnouncementHost was found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::MakeHost()
|
||||
{
|
||||
if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr)
|
||||
{
|
||||
assert(false && L"No host producer has been assigned.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NarratorAnnouncementHostFactory::s_hostProducer->MakeHost();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NarratorAnnouncementHostFactory class.
|
||||
// This class exists to hide the construction of a concrete INarratorAnnouncementHost.
|
||||
// Depending on the version of the OS the app is running on, the factory will return
|
||||
// an announcement host appropriate for that version.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
class NarratorAnnouncementHostFactory
|
||||
{
|
||||
public:
|
||||
static INarratorAnnouncementHost ^ MakeHost();
|
||||
|
||||
private:
|
||||
NarratorAnnouncementHostFactory()
|
||||
{
|
||||
}
|
||||
|
||||
static int Initialize();
|
||||
static void RegisterHosts();
|
||||
static INarratorAnnouncementHost ^ GetHostProducer();
|
||||
|
||||
private:
|
||||
static int s_init;
|
||||
static INarratorAnnouncementHost ^ s_hostProducer;
|
||||
static std::vector<INarratorAnnouncementHost ^> s_hosts;
|
||||
};
|
||||
}
|
97
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
97
src/CalcViewModel/Common/Automation/NotificationHost.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "NotificationHost.h"
|
||||
|
||||
using namespace CalculatorApp::Common::Automation;
|
||||
using namespace Windows::Foundation::Metadata;
|
||||
using namespace Windows::UI::Xaml::Automation;
|
||||
using namespace Windows::UI::Xaml::Automation::Peers;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
||||
NotificationHost::NotificationHost()
|
||||
: m_host(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool NotificationHost::IsHostAvailable()
|
||||
{
|
||||
return ApiInformation::IsMethodPresent(L"Windows.UI.Xaml.Automation.Peers.AutomationPeer", L"RaiseNotificationEvent");
|
||||
}
|
||||
|
||||
INarratorAnnouncementHost ^ NotificationHost::MakeHost()
|
||||
{
|
||||
return ref new NotificationHost();
|
||||
}
|
||||
|
||||
void NotificationHost::Announce(NarratorAnnouncement ^ announcement)
|
||||
{
|
||||
if (m_host == nullptr)
|
||||
{
|
||||
m_host = ref new TextBlock();
|
||||
}
|
||||
|
||||
auto peer = FrameworkElementAutomationPeer::FromElement(m_host);
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->RaiseNotificationEvent(
|
||||
GetWindowsNotificationKind(announcement->Kind),
|
||||
GetWindowsNotificationProcessing(announcement->Processing),
|
||||
announcement->Announcement,
|
||||
announcement->ActivityId);
|
||||
}
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(CustomPeers::AutomationNotificationKind customKindType)
|
||||
{
|
||||
switch (customKindType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationKind::ItemAdded:
|
||||
return StandardPeers::AutomationNotificationKind::ItemAdded;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ItemRemoved:
|
||||
return StandardPeers::AutomationNotificationKind::ItemRemoved;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionCompleted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionCompleted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::ActionAborted:
|
||||
return StandardPeers::AutomationNotificationKind::ActionAborted;
|
||||
|
||||
case CustomPeers::AutomationNotificationKind::Other:
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationKind");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationKind::Other;
|
||||
}
|
||||
|
||||
StandardPeers::AutomationNotificationProcessing
|
||||
NotificationHost::GetWindowsNotificationProcessing(CustomPeers::AutomationNotificationProcessing customProcessingType)
|
||||
{
|
||||
switch (customProcessingType)
|
||||
{
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantAll:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantAll;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::ImportantMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::All:
|
||||
return StandardPeers::AutomationNotificationProcessing::All;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::MostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::MostRecent;
|
||||
|
||||
case CustomPeers::AutomationNotificationProcessing::CurrentThenMostRecent:
|
||||
return StandardPeers::AutomationNotificationProcessing::CurrentThenMostRecent;
|
||||
|
||||
default:
|
||||
assert(false && L"Unexpected AutomationNotificationProcessing");
|
||||
}
|
||||
|
||||
return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent;
|
||||
}
|
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
34
src/CalcViewModel/Common/Automation/NotificationHost.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "INarratorAnnouncementHost.h"
|
||||
|
||||
// Declaration of the NotificationHost class.
|
||||
// This class announces NarratorAnnouncements using the RaiseNotification API
|
||||
// available in RS3.
|
||||
|
||||
namespace CalculatorApp::Common::Automation
|
||||
{
|
||||
public
|
||||
ref class NotificationHost sealed : public INarratorAnnouncementHost
|
||||
{
|
||||
public:
|
||||
NotificationHost();
|
||||
|
||||
virtual bool IsHostAvailable();
|
||||
virtual INarratorAnnouncementHost ^ MakeHost();
|
||||
|
||||
virtual void Announce(NarratorAnnouncement ^ announcement);
|
||||
|
||||
private:
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind
|
||||
GetWindowsNotificationKind(CalculatorApp::Common::Automation::AutomationNotificationKind customKindType);
|
||||
|
||||
static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing
|
||||
GetWindowsNotificationProcessing(CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType);
|
||||
|
||||
private:
|
||||
Windows::UI::Xaml::UIElement ^ m_host;
|
||||
};
|
||||
}
|
|
@ -120,7 +120,7 @@ public
|
|||
RshL = (int)CM::Command::CommandRSHFL,
|
||||
RolC = (int)CM::Command::CommandROLC,
|
||||
RorC = (int)CM::Command::CommandRORC,
|
||||
|
||||
|
||||
BINSTART = (int)CM::Command::CommandBINEDITSTART,
|
||||
BINPOS0 = (int)CM::Command::CommandBINPOS0,
|
||||
BINPOS1 = (int)CM::Command::CommandBINPOS1,
|
||||
|
@ -194,6 +194,14 @@ public
|
|||
MemoryRecall = (int)CM::Command::CommandRECALL,
|
||||
MemoryClear = (int)CM::Command::CommandMCLEAR,
|
||||
BitflipButton = 1000,
|
||||
FullKeypadButton = 1001
|
||||
FullKeypadButton = 1001,
|
||||
|
||||
// Buttons used in graphing calculator
|
||||
LessThan,
|
||||
LessThanOrEqualTo,
|
||||
GreaterThan,
|
||||
GreaterThanOrEqualTo,
|
||||
X,
|
||||
Y
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
using namespace Concurrency;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::UI::Xaml::Controls;
|
||||
|
@ -43,10 +44,15 @@ static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlS
|
|||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyInverseChordsForButtons;
|
||||
static multimap<int, multimap<MyVirtualKey, WeakReference>> s_VirtualKeyControlInverseChordsForButtons;
|
||||
|
||||
static multimap<int, bool> s_ShiftKeyPressed;
|
||||
static multimap<int, bool> s_ControlKeyPressed;
|
||||
static multimap<int, bool> s_ShiftButtonChecked;
|
||||
static multimap<int, bool> s_IsDropDownOpen;
|
||||
static map<int, bool> s_ShiftKeyPressed;
|
||||
static map<int, bool> s_ControlKeyPressed;
|
||||
static map<int, bool> s_ShiftButtonChecked;
|
||||
static map<int, bool> s_IsDropDownOpen;
|
||||
|
||||
static map<int, bool> s_ignoreNextEscape;
|
||||
static map<int, bool> s_keepIgnoringEscape;
|
||||
static map<int, bool> s_fHonorShortcuts;
|
||||
static map<int, Flyout ^> s_AboutFlyout;
|
||||
|
||||
static reader_writer_lock s_keyboardShortcutMapLock;
|
||||
|
||||
|
@ -157,12 +163,6 @@ namespace CalculatorApp
|
|||
}
|
||||
}
|
||||
|
||||
static multimap<int, bool> s_ignoreNextEscape;
|
||||
static multimap<int, bool> s_keepIgnoringEscape;
|
||||
static multimap<int, bool> s_fHonorShortcuts;
|
||||
static multimap<int, bool> s_fHandledEnter;
|
||||
static multimap<int, Flyout ^> s_AboutFlyout;
|
||||
|
||||
void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
|
||||
{
|
||||
// Writer lock for the static maps
|
||||
|
@ -172,14 +172,12 @@ void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce)
|
|||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, true));
|
||||
s_ignoreNextEscape[viewId] = true;
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce));
|
||||
s_keepIgnoringEscape[viewId] = !onlyOnce;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,14 +190,12 @@ void KeyboardShortcutManager::HonorEscape()
|
|||
|
||||
if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end())
|
||||
{
|
||||
s_ignoreNextEscape.erase(viewId);
|
||||
s_ignoreNextEscape.insert(std::make_pair(viewId, false));
|
||||
s_ignoreNextEscape[viewId] = false;
|
||||
}
|
||||
|
||||
if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end())
|
||||
{
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.insert(std::make_pair(viewId, false));
|
||||
s_keepIgnoringEscape[viewId] = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,7 +426,6 @@ void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow ^ sender, Ch
|
|||
{
|
||||
wchar_t character = static_cast<wchar_t>(args->KeyCode);
|
||||
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);
|
||||
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
LightUpButtons(buttons);
|
||||
|
@ -474,8 +469,8 @@ const std::multimap<MyVirtualKey, WeakReference>& GetCurrentKeyDictionary(MyVirt
|
|||
}
|
||||
else
|
||||
{
|
||||
auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end())
|
||||
auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId);
|
||||
if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end())
|
||||
{
|
||||
for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator)
|
||||
{
|
||||
|
@ -558,8 +553,7 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs
|
|||
|
||||
if (currControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, true));
|
||||
s_ControlKeyPressed[viewId] = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -572,26 +566,24 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs
|
|||
|
||||
if (currShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, true));
|
||||
s_ShiftKeyPressed[viewId] = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(static_cast<MyVirtualKey>(key));
|
||||
auto buttons = lookupMap.equal_range(static_cast<MyVirtualKey>(key));
|
||||
|
||||
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
|
||||
|
||||
if (currentHonorShortcuts != s_fHonorShortcuts.end())
|
||||
{
|
||||
if (currentHonorShortcuts->second)
|
||||
{
|
||||
const auto myVirtualKey = static_cast<MyVirtualKey>(key);
|
||||
const auto& lookupMap = GetCurrentKeyDictionary(myVirtualKey);
|
||||
auto buttons = lookupMap.equal_range(myVirtualKey);
|
||||
RunFirstEnabledButtonCommand(buttons);
|
||||
|
||||
// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V
|
||||
// is pressed. When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open. Ctrl+Insert is
|
||||
// equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
|
||||
auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId);
|
||||
if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second)
|
||||
{
|
||||
// Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed
|
||||
|
@ -620,8 +612,7 @@ void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow ^ sender, KeyEventArgs ^
|
|||
|
||||
if (currentShiftKeyPressed != s_ShiftKeyPressed.end())
|
||||
{
|
||||
s_ShiftKeyPressed.erase(viewId);
|
||||
s_ShiftKeyPressed.insert(std::make_pair(viewId, false));
|
||||
s_ShiftKeyPressed[viewId] = false;
|
||||
}
|
||||
}
|
||||
else if (key == VirtualKey::Control)
|
||||
|
@ -633,8 +624,7 @@ void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow ^ sender, KeyEventArgs ^
|
|||
|
||||
if (currControlKeyPressed != s_ControlKeyPressed.end())
|
||||
{
|
||||
s_ControlKeyPressed.erase(viewId);
|
||||
s_ControlKeyPressed.insert(std::make_pair(viewId, false));
|
||||
s_ControlKeyPressed[viewId] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -712,8 +702,7 @@ void KeyboardShortcutManager::ShiftButtonChecked(bool checked)
|
|||
|
||||
if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end())
|
||||
{
|
||||
s_ShiftButtonChecked.erase(viewId);
|
||||
s_ShiftButtonChecked.insert(std::make_pair(viewId, checked));
|
||||
s_ShiftButtonChecked[viewId] = checked;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -723,8 +712,7 @@ void KeyboardShortcutManager::UpdateDropDownState(bool isOpen)
|
|||
|
||||
if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end())
|
||||
{
|
||||
s_IsDropDownOpen.erase(viewId);
|
||||
s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen));
|
||||
s_IsDropDownOpen[viewId] = isOpen;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -734,8 +722,7 @@ void KeyboardShortcutManager::UpdateDropDownState(Flyout ^ aboutPageFlyout)
|
|||
|
||||
if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end())
|
||||
{
|
||||
s_AboutFlyout.erase(viewId);
|
||||
s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout));
|
||||
s_AboutFlyout[viewId] = aboutPageFlyout;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -748,19 +735,7 @@ void KeyboardShortcutManager::HonorShortcuts(bool allow)
|
|||
|
||||
if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end())
|
||||
{
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHonorShortcuts.insert(std::make_pair(viewId, allow));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::HandledEnter(bool ishandled)
|
||||
{
|
||||
int viewId = Utils::GetWindowId();
|
||||
|
||||
if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end())
|
||||
{
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_fHandledEnter.insert(std::make_pair(viewId, ishandled));
|
||||
s_fHonorShortcuts[viewId] = allow;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -812,15 +787,14 @@ void KeyboardShortcutManager::RegisterNewAppViewId()
|
|||
s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap<MyVirtualKey, WeakReference>()));
|
||||
}
|
||||
|
||||
s_ShiftKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ControlKeyPressed.insert(std::make_pair(appViewId, false));
|
||||
s_ShiftButtonChecked.insert(std::make_pair(appViewId, false));
|
||||
s_IsDropDownOpen.insert(std::make_pair(appViewId, false));
|
||||
s_ignoreNextEscape.insert(std::make_pair(appViewId, false));
|
||||
s_keepIgnoringEscape.insert(std::make_pair(appViewId, false));
|
||||
s_fHonorShortcuts.insert(std::make_pair(appViewId, true));
|
||||
s_fHandledEnter.insert(std::make_pair(appViewId, true));
|
||||
s_AboutFlyout.insert(std::make_pair(appViewId, nullptr));
|
||||
s_ShiftKeyPressed[appViewId] = false;
|
||||
s_ControlKeyPressed[appViewId] = false;
|
||||
s_ShiftButtonChecked[appViewId] = false;
|
||||
s_IsDropDownOpen[appViewId] = false;
|
||||
s_ignoreNextEscape[appViewId] = false;
|
||||
s_keepIgnoringEscape[appViewId] = false;
|
||||
s_fHonorShortcuts[appViewId] = true;
|
||||
s_AboutFlyout[appViewId] = nullptr;
|
||||
}
|
||||
|
||||
void KeyboardShortcutManager::OnWindowClosed(int viewId)
|
||||
|
@ -845,6 +819,5 @@ void KeyboardShortcutManager::OnWindowClosed(int viewId)
|
|||
s_ignoreNextEscape.erase(viewId);
|
||||
s_keepIgnoringEscape.erase(viewId);
|
||||
s_fHonorShortcuts.erase(viewId);
|
||||
s_fHandledEnter.erase(viewId);
|
||||
s_AboutFlyout.erase(viewId);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ namespace CalculatorApp
|
|||
static void IgnoreEscape(bool onlyOnce);
|
||||
static void HonorEscape();
|
||||
static void HonorShortcuts(bool allow);
|
||||
static void HandledEnter(bool ishandled);
|
||||
static void UpdateDropDownState(bool);
|
||||
static void ShiftButtonChecked(bool checked);
|
||||
static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout ^ aboutPageFlyout);
|
||||
|
|
|
@ -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;
|
||||
|
@ -46,145 +41,266 @@ static constexpr int DATA_ID = 13;
|
|||
static constexpr int PRESSURE_ID = 14;
|
||||
static constexpr int ANGLE_ID = 15;
|
||||
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, 17> 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::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)
|
||||
|
@ -211,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;
|
||||
}
|
||||
}
|
||||
|
@ -228,9 +352,13 @@ bool NavCategory::IsValidViewMode(ViewMode mode)
|
|||
|
||||
bool NavCategory::IsCalculatorViewMode(ViewMode mode)
|
||||
{
|
||||
// Historically, Date Calculator is not a Calculator mode
|
||||
// even though it is in the Calculator category.
|
||||
return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
|
||||
// Historically, Calculator modes are Standard, Scientific, and Programmer.
|
||||
return !IsDateCalculatorViewMode(mode) && !IsGraphingCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator);
|
||||
}
|
||||
|
||||
bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode)
|
||||
{
|
||||
return mode == ViewMode::Graphing;
|
||||
}
|
||||
|
||||
bool NavCategory::IsDateCalculatorViewMode(ViewMode mode)
|
||||
|
@ -389,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -407,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" });
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ namespace CalculatorApp
|
|||
Data = 13,
|
||||
Pressure = 14,
|
||||
Angle = 15,
|
||||
Currency = 16
|
||||
Currency = 16,
|
||||
Graphing = 17
|
||||
};
|
||||
|
||||
public
|
||||
|
@ -66,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)
|
||||
|
@ -74,7 +77,9 @@ namespace CalculatorApp
|
|||
, glyph(glyph)
|
||||
, groupType(group)
|
||||
, virtualKey(vKey)
|
||||
, accessKey(aKey)
|
||||
, supportsNegative(categorySupportsNegative)
|
||||
, isEnabled(enabled)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -85,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
|
||||
|
@ -109,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);
|
||||
|
@ -156,6 +135,7 @@ namespace CalculatorApp
|
|||
|
||||
static bool IsValidViewMode(ViewMode mode);
|
||||
static bool IsCalculatorViewMode(ViewMode mode);
|
||||
static bool IsGraphingCalculatorViewMode(ViewMode mode);
|
||||
static bool IsDateCalculatorViewMode(ViewMode mode);
|
||||
static bool IsConverterViewMode(ViewMode mode);
|
||||
|
||||
|
@ -178,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();
|
||||
|
@ -195,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
|
||||
|
@ -216,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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
using namespace CalculatorApp;
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace concurrency;
|
||||
using namespace Graphing::Renderer;
|
||||
using namespace Platform;
|
||||
using namespace std;
|
||||
using namespace Utils;
|
||||
using namespace Windows::ApplicationModel::Resources;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::UI;
|
||||
using namespace Windows::UI::Core;
|
||||
using namespace Windows::UI::ViewManagement;
|
||||
using namespace Windows::UI::Xaml;
|
||||
|
@ -196,3 +198,118 @@ task<String ^> Utils::ReadFileFromFolder(IStorageFolder ^ folder, String ^ fileN
|
|||
String ^ contents = co_await FileIO::ReadTextAsync(file);
|
||||
co_return contents;
|
||||
}
|
||||
|
||||
bool Utils::AreColorsEqual(const Color& color1, const Color& color2)
|
||||
{
|
||||
return ((color1.A == color2.A)
|
||||
&& (color1.R == color2.R)
|
||||
&& (color1.G == color2.G)
|
||||
&& (color1.B == color2.B));
|
||||
}
|
||||
|
||||
String^ Utils::Trim(String^ value)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wstring trimmed = value->Data();
|
||||
Trim(trimmed);
|
||||
return ref new String(trimmed.c_str());
|
||||
}
|
||||
|
||||
void Utils::Trim(wstring& value)
|
||||
{
|
||||
TrimFront(value);
|
||||
TrimBack(value);
|
||||
}
|
||||
|
||||
void Utils::TrimFront(wstring& value)
|
||||
{
|
||||
value.erase(value.begin(), find_if(value.cbegin(), value.cend(), [](int ch){
|
||||
return !isspace(ch);
|
||||
}));
|
||||
}
|
||||
|
||||
void Utils::TrimBack(wstring& value)
|
||||
{
|
||||
value.erase(find_if(value.crbegin(), value.crend(), [](int ch) {
|
||||
return !isspace(ch);
|
||||
}).base(), value.end());
|
||||
}
|
||||
|
||||
String^ Utils::EscapeHtmlSpecialCharacters(String^ originalString, shared_ptr<vector<wchar_t>> specialCharacters)
|
||||
{
|
||||
// Construct a default special characters if not provided.
|
||||
if (specialCharacters == nullptr)
|
||||
{
|
||||
specialCharacters = make_shared<vector<wchar_t>>();
|
||||
specialCharacters->push_back(L'&');
|
||||
specialCharacters->push_back(L'\"');
|
||||
specialCharacters->push_back(L'\'');
|
||||
specialCharacters->push_back(L'<');
|
||||
specialCharacters->push_back(L'>');
|
||||
}
|
||||
|
||||
bool replaceCharacters = false;
|
||||
const wchar_t* pCh;
|
||||
String^ replacementString = nullptr;
|
||||
|
||||
// First step is scanning the string for special characters.
|
||||
// If there isn't any special character, we simply return the original string
|
||||
for (pCh = originalString->Data(); *pCh; pCh++)
|
||||
{
|
||||
if (std::find(specialCharacters->begin(), specialCharacters->end(), *pCh) != specialCharacters->end())
|
||||
{
|
||||
replaceCharacters = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replaceCharacters)
|
||||
{
|
||||
// If we indeed find a special character, we step back one character (the special
|
||||
// character), and we create a new string where we replace those characters one by one
|
||||
pCh--;
|
||||
wstringstream buffer;
|
||||
buffer << wstring(originalString->Data(), pCh);
|
||||
|
||||
for (; *pCh; pCh++)
|
||||
{
|
||||
switch (*pCh)
|
||||
{
|
||||
case L'&':
|
||||
buffer << L"&";
|
||||
break;
|
||||
case L'\"':
|
||||
buffer << L""";
|
||||
break;
|
||||
case L'\'':
|
||||
buffer << L"'";
|
||||
break;
|
||||
case L'<':
|
||||
buffer << L"<";
|
||||
break;
|
||||
case L'>':
|
||||
buffer << L">";
|
||||
break;
|
||||
default:
|
||||
buffer << *pCh;
|
||||
}
|
||||
}
|
||||
replacementString = ref new String(buffer.str().c_str());
|
||||
}
|
||||
|
||||
return replaceCharacters ? replacementString : originalString;
|
||||
}
|
||||
|
||||
bool operator==(const Color& color1, const Color& color2)
|
||||
{
|
||||
return equal_to<Color>()(color1, color2);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& color1, const Color& color2)
|
||||
{
|
||||
return !(color1 == color2);
|
||||
}
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
|
||||
#include "CalcManager/ExpressionCommandInterface.h"
|
||||
#include "DelegateCommand.h"
|
||||
#include "GraphingInterfaces/GraphingEnums.h"
|
||||
|
||||
// Utility macros to make Models easier to write
|
||||
// generates a member variable called m_<n>
|
||||
|
||||
#define SINGLE_ARG(...) __VA_ARGS__
|
||||
|
||||
#define PROPERTY_R(t, n) \
|
||||
property t n \
|
||||
{ \
|
||||
|
@ -405,12 +409,18 @@ namespace Utils
|
|||
Windows::Foundation::DateTime GetUniversalSystemTime();
|
||||
bool IsDateTimeOlderThan(Windows::Foundation::DateTime dateTime, const long long duration);
|
||||
|
||||
concurrency::task<void> WriteFileToFolder(
|
||||
Windows::Storage::IStorageFolder ^ folder,
|
||||
Platform::String ^ fileName,
|
||||
Platform::String ^ contents,
|
||||
Windows::Storage::CreationCollisionOption collisionOption);
|
||||
concurrency::task<Platform::String ^> ReadFileFromFolder(Windows::Storage::IStorageFolder ^ folder, Platform::String ^ fileName);
|
||||
concurrency::task<void> WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption);
|
||||
concurrency::task<Platform::String^> ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName);
|
||||
|
||||
bool AreColorsEqual(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
|
||||
|
||||
Platform::String^ Trim(Platform::String^ value);
|
||||
void Trim(std::wstring& value);
|
||||
void TrimFront(std::wstring& value);
|
||||
void TrimBack(std::wstring& value);
|
||||
|
||||
Platform::String ^ EscapeHtmlSpecialCharacters(Platform::String ^ originalString, std::shared_ptr<std::vector<wchar_t>> specialCharacters = nullptr);
|
||||
|
||||
}
|
||||
|
||||
// This goes into the header to define the property, in the public: section of the class
|
||||
|
@ -707,3 +717,18 @@ namespace CalculatorApp
|
|||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
// There's no standard definition of equality for Windows::UI::Color structs.
|
||||
// Define a template specialization for std::equal_to.
|
||||
template<>
|
||||
class std::equal_to<Windows::UI::Color>
|
||||
{
|
||||
public:
|
||||
bool operator()(const Windows::UI::Color& color1, const Windows::UI::Color& color2)
|
||||
{
|
||||
return Utils::AreColorsEqual(color1, color2);
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
|
||||
bool operator!=(const Windows::UI::Color& color1, const Windows::UI::Color& color2);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
|
302
src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp
Normal file
302
src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp
Normal file
|
@ -0,0 +1,302 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "EquationViewModel.h"
|
||||
#include "CalcViewModel\Common\LocalizationSettings.h"
|
||||
#include "CalcViewModel\GraphingCalculatorEnums.h"
|
||||
|
||||
using namespace CalculatorApp::Common;
|
||||
using namespace Graphing;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace std;
|
||||
using namespace Windows::UI;
|
||||
using namespace Windows::UI::Xaml;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace GraphControl;
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
GridDisplayItems::GridDisplayItems()
|
||||
: m_Expression{ "" }
|
||||
, m_Direction{ "" }
|
||||
{
|
||||
}
|
||||
|
||||
KeyGraphFeaturesItem::KeyGraphFeaturesItem()
|
||||
: m_Title{ "" }
|
||||
, m_DisplayItems{ ref new Vector<String ^>() }
|
||||
, m_GridItems{ ref new Vector<GridDisplayItems ^>() }
|
||||
, m_IsText{ false }
|
||||
{
|
||||
}
|
||||
|
||||
EquationViewModel::EquationViewModel(Equation ^ equation, int functionLabelIndex, Windows::UI::Color color)
|
||||
: m_AnalysisErrorVisible{ false }
|
||||
, m_FunctionLabelIndex{ functionLabelIndex }
|
||||
, m_KeyGraphFeaturesItems{ ref new Vector<KeyGraphFeaturesItem ^>() }
|
||||
, m_resourceLoader{ Windows::ApplicationModel::Resources::ResourceLoader::GetForCurrentView() }
|
||||
{
|
||||
if (equation == nullptr)
|
||||
{
|
||||
throw ref new InvalidArgumentException(L"Equation cannot be null");
|
||||
}
|
||||
|
||||
GraphEquation = equation;
|
||||
LineColor = color;
|
||||
IsLineEnabled = true;
|
||||
}
|
||||
|
||||
void EquationViewModel::PopulateKeyGraphFeatures(KeyGraphFeaturesInfo ^ graphEquation)
|
||||
{
|
||||
if (graphEquation->AnalysisError != 0)
|
||||
{
|
||||
AnalysisErrorVisible = true;
|
||||
if (graphEquation->AnalysisError == static_cast<int>(AnalysisErrorType::AnalysisCouldNotBePerformed))
|
||||
{
|
||||
AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisCouldNotBePerformed");
|
||||
}
|
||||
else if (graphEquation->AnalysisError == static_cast<int>(AnalysisErrorType::AnalysisNotSupported))
|
||||
{
|
||||
AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisNotSupported");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
KeyGraphFeaturesItems->Clear();
|
||||
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"Domain"), graphEquation->Domain, m_resourceLoader->GetString(L"KGFDomainNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"Range"), graphEquation->Range, m_resourceLoader->GetString(L"KGFRangeNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"XIntercept"), graphEquation->XIntercept, m_resourceLoader->GetString(L"KGFXInterceptNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"YIntercept"), graphEquation->YIntercept, m_resourceLoader->GetString(L"KGFYInterceptNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"Minima"), graphEquation->Minima, m_resourceLoader->GetString(L"KGFMinimaNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"Maxima"), graphEquation->Maxima, m_resourceLoader->GetString(L"KGFMaximaNone"));
|
||||
AddKeyGraphFeature(m_resourceLoader->GetString(L"InflectionPoints"), graphEquation->InflectionPoints, m_resourceLoader->GetString(L"KGFInflectionPointsNone"));
|
||||
AddKeyGraphFeature(
|
||||
m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone"));
|
||||
AddKeyGraphFeature(
|
||||
m_resourceLoader->GetString(L"HorizontalAsymptotes"), graphEquation->HorizontalAsymptotes, m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone"));
|
||||
AddKeyGraphFeature(
|
||||
m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone"));
|
||||
AddParityKeyGraphFeature(graphEquation);
|
||||
AddPeriodicityKeyGraphFeature(graphEquation);
|
||||
AddMonotoncityKeyGraphFeature(graphEquation);
|
||||
AddTooComplexKeyGraphFeature(graphEquation);
|
||||
|
||||
AnalysisErrorVisible = false;
|
||||
}
|
||||
|
||||
void EquationViewModel::AddKeyGraphFeature(String ^ title, String ^ expression, String ^ errorString)
|
||||
{
|
||||
KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem();
|
||||
item->Title = title;
|
||||
if (expression != L"")
|
||||
{
|
||||
item->DisplayItems->Append(expression);
|
||||
item->IsText = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->DisplayItems->Append(errorString);
|
||||
item->IsText = true;
|
||||
}
|
||||
KeyGraphFeaturesItems->Append(item);
|
||||
}
|
||||
|
||||
void EquationViewModel::AddKeyGraphFeature(String ^ title, IVector<String ^> ^ expressionVector, String ^ errorString)
|
||||
{
|
||||
KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem();
|
||||
item->Title = title;
|
||||
if (expressionVector->Size != 0)
|
||||
{
|
||||
for (auto expression : expressionVector)
|
||||
{
|
||||
item->DisplayItems->Append(expression);
|
||||
}
|
||||
item->IsText = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->DisplayItems->Append(errorString);
|
||||
item->IsText = true;
|
||||
}
|
||||
KeyGraphFeaturesItems->Append(item);
|
||||
}
|
||||
|
||||
void EquationViewModel::AddParityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
|
||||
{
|
||||
KeyGraphFeaturesItem ^ parityItem = ref new KeyGraphFeaturesItem();
|
||||
parityItem->Title = m_resourceLoader->GetString(L"Parity");
|
||||
switch (graphEquation->Parity)
|
||||
{
|
||||
case 0:
|
||||
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown"));
|
||||
break;
|
||||
case 1:
|
||||
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityOdd"));
|
||||
break;
|
||||
case 2:
|
||||
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityEven"));
|
||||
break;
|
||||
case 3:
|
||||
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityNeither"));
|
||||
break;
|
||||
default:
|
||||
parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown"));
|
||||
}
|
||||
parityItem->IsText = true;
|
||||
|
||||
KeyGraphFeaturesItems->Append(parityItem);
|
||||
}
|
||||
|
||||
void EquationViewModel::AddPeriodicityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
|
||||
{
|
||||
KeyGraphFeaturesItem ^ periodicityItem = ref new KeyGraphFeaturesItem();
|
||||
periodicityItem->Title = m_resourceLoader->GetString(L"Periodicity");
|
||||
switch (graphEquation->PeriodicityDirection)
|
||||
{
|
||||
case 0:
|
||||
// Periodicity is not supported or is too complex to calculate.
|
||||
// Return out of this function without adding periodicity to KeyGraphFeatureItems.
|
||||
// SetTooComplexFeaturesErrorProperty will set the too complex error when periodicity is supported and unknown
|
||||
return;
|
||||
case 1:
|
||||
if (graphEquation->PeriodicityExpression == L"")
|
||||
{
|
||||
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityUnknown"));
|
||||
periodicityItem->IsText = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
periodicityItem->DisplayItems->Append(graphEquation->PeriodicityExpression);
|
||||
periodicityItem->IsText = false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityNotPeriodic"));
|
||||
periodicityItem->IsText = false;
|
||||
break;
|
||||
default:
|
||||
periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityError"));
|
||||
periodicityItem->IsText = true;
|
||||
}
|
||||
|
||||
KeyGraphFeaturesItems->Append(periodicityItem);
|
||||
}
|
||||
|
||||
void EquationViewModel::AddMonotoncityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
|
||||
{
|
||||
KeyGraphFeaturesItem ^ monotonicityItem = ref new KeyGraphFeaturesItem();
|
||||
monotonicityItem->Title = m_resourceLoader->GetString(L"Monotonicity");
|
||||
if (graphEquation->Monotonicity->Size != 0)
|
||||
{
|
||||
for (auto item : graphEquation->Monotonicity)
|
||||
{
|
||||
GridDisplayItems ^ gridItem = ref new GridDisplayItems();
|
||||
gridItem->Expression = item->Key;
|
||||
|
||||
auto monotonicityType = item->Value->Data();
|
||||
switch (*monotonicityType)
|
||||
{
|
||||
case '0':
|
||||
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityUnknown");
|
||||
break;
|
||||
case '1':
|
||||
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityIncreasing");
|
||||
break;
|
||||
case '2':
|
||||
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityDecreasing");
|
||||
break;
|
||||
case '3':
|
||||
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityConstant");
|
||||
break;
|
||||
default:
|
||||
gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityError");
|
||||
break;
|
||||
}
|
||||
|
||||
monotonicityItem->GridItems->Append(gridItem);
|
||||
}
|
||||
monotonicityItem->IsText = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
monotonicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFMonotonicityError"));
|
||||
monotonicityItem->IsText = true;
|
||||
}
|
||||
|
||||
KeyGraphFeaturesItems->Append(monotonicityItem);
|
||||
}
|
||||
|
||||
void EquationViewModel::AddTooComplexKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation)
|
||||
{
|
||||
if (graphEquation->TooComplexFeatures <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Platform::String ^ separator = ref new String(LocalizationSettings::GetInstance().GetListSeparator().c_str());
|
||||
|
||||
wstring error;
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Domain") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Range") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"XIntercept") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"YIntercept") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Parity") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Periodicity") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Minima") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Maxima") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"InflectionPoints") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"VerticalAsymptotes") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"HorizontalAsymptotes") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"ObliqueAsymptotes") + separator + L" ")->Data());
|
||||
}
|
||||
if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals)
|
||||
{
|
||||
error.append((m_resourceLoader->GetString(L"Monotonicity") + separator + L" ")->Data());
|
||||
}
|
||||
|
||||
KeyGraphFeaturesItem ^ tooComplexItem = ref new KeyGraphFeaturesItem();
|
||||
tooComplexItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFTooComplexFeaturesError"));
|
||||
tooComplexItem->DisplayItems->Append(ref new String(error.substr(0, (error.length() - (separator->Length() + 1))).c_str()));
|
||||
tooComplexItem->IsText = true;
|
||||
|
||||
KeyGraphFeaturesItems->Append(tooComplexItem);
|
||||
}
|
||||
}
|
120
src/CalcViewModel/GraphingCalculator/EquationViewModel.h
Normal file
120
src/CalcViewModel/GraphingCalculator/EquationViewModel.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/Utils.h"
|
||||
|
||||
namespace GraphControl
|
||||
{
|
||||
ref class Equation;
|
||||
ref class KeyGraphFeaturesInfo;
|
||||
}
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
public
|
||||
ref class GridDisplayItems sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
GridDisplayItems();
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String ^, Expression);
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String ^, Direction);
|
||||
};
|
||||
|
||||
public
|
||||
ref class KeyGraphFeaturesItem sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
KeyGraphFeaturesItem();
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_RW(Platform::String ^, Title);
|
||||
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<Platform::String ^> ^, DisplayItems);
|
||||
OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector<GridDisplayItems ^> ^, GridItems);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsText);
|
||||
};
|
||||
|
||||
public
|
||||
ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
EquationViewModel(GraphControl::Equation ^ equation, int functionLabelIndex, Windows::UI::Color color);
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_R(GraphControl::Equation ^, GraphEquation);
|
||||
OBSERVABLE_PROPERTY_R(int, FunctionLabelIndex);
|
||||
OBSERVABLE_PROPERTY_RW(bool, IsLastItemInList);
|
||||
|
||||
property Platform::String ^ Expression
|
||||
{
|
||||
Platform::String ^ get()
|
||||
{
|
||||
return GraphEquation->Expression;
|
||||
}
|
||||
void set(Platform::String ^ value)
|
||||
{
|
||||
if (GraphEquation->Expression != value)
|
||||
{
|
||||
GraphEquation->Expression = value;
|
||||
RaisePropertyChanged("Expression");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property Windows::UI::Color LineColor
|
||||
{
|
||||
Windows::UI::Color get()
|
||||
{
|
||||
return GraphEquation->LineColor;
|
||||
}
|
||||
void set(Windows::UI::Color value)
|
||||
{
|
||||
if (!Utils::AreColorsEqual(GraphEquation->LineColor, value))
|
||||
{
|
||||
GraphEquation->LineColor = value;
|
||||
RaisePropertyChanged("LineColor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool IsLineEnabled
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return GraphEquation->IsLineEnabled;
|
||||
}
|
||||
void set(bool value)
|
||||
{
|
||||
if (GraphEquation->IsLineEnabled != value)
|
||||
{
|
||||
GraphEquation->IsLineEnabled = value;
|
||||
RaisePropertyChanged("IsLineEnabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Key Graph Features
|
||||
OBSERVABLE_PROPERTY_R(Platform::String ^, AnalysisErrorString);
|
||||
OBSERVABLE_PROPERTY_R(bool, AnalysisErrorVisible);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<CalculatorApp::ViewModel::KeyGraphFeaturesItem ^> ^, KeyGraphFeaturesItems)
|
||||
|
||||
void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info);
|
||||
|
||||
private:
|
||||
void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString);
|
||||
void AddKeyGraphFeature(
|
||||
Platform::String ^ title,
|
||||
Windows::Foundation::Collections::IVector<Platform::String ^> ^ expressionVector,
|
||||
Platform::String ^ errorString);
|
||||
void AddParityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
|
||||
void AddPeriodicityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
|
||||
void AddMonotoncityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
|
||||
void AddTooComplexKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info);
|
||||
|
||||
Windows::Foundation::Collections::IObservableMap<Platform::String ^, Platform::String ^> ^ m_Monotonicity;
|
||||
Windows::ApplicationModel::Resources::ResourceLoader ^ m_resourceLoader;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GraphingCalculatorViewModel.h"
|
||||
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace Platform;
|
||||
using namespace Platform::Collections;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
using namespace Windows::UI::Xaml::Data;
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
GraphingCalculatorViewModel::GraphingCalculatorViewModel()
|
||||
: m_IsDecimalEnabled{ true }
|
||||
, m_Equations{ ref new Vector<EquationViewModel ^>() }
|
||||
, m_Variables{ ref new Vector<VariableViewModel ^>() }
|
||||
{
|
||||
}
|
||||
|
||||
void GraphingCalculatorViewModel::OnButtonPressed(Object ^ parameter)
|
||||
{
|
||||
}
|
||||
|
||||
void GraphingCalculatorViewModel::UpdateVariables(IMap<String ^, double> ^ variables)
|
||||
{
|
||||
Variables->Clear();
|
||||
for (auto var : variables)
|
||||
{
|
||||
auto variable = ref new VariableViewModel(var->Key, var->Value);
|
||||
variable->VariableUpdated += ref new EventHandler<VariableChangedEventArgs>([this, variable](Object ^ sender, VariableChangedEventArgs e) {
|
||||
VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue });
|
||||
});
|
||||
Variables->Append(variable);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphingCalculatorViewModel::SetSelectedEquation(EquationViewModel ^ equation)
|
||||
{
|
||||
SelectedEquation = equation;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/Utils.h"
|
||||
#include "EquationViewModel.h"
|
||||
#include "VariableViewModel.h"
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
[Windows::UI::Xaml::Data::Bindable] public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
GraphingCalculatorViewModel();
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<EquationViewModel ^> ^, Equations);
|
||||
OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector<VariableViewModel ^> ^, Variables);
|
||||
OBSERVABLE_PROPERTY_R(EquationViewModel ^, SelectedEquation);
|
||||
|
||||
COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed);
|
||||
|
||||
event Windows::Foundation::EventHandler<VariableChangedEventArgs> ^ VariableUpdated;
|
||||
|
||||
void UpdateVariables(Windows::Foundation::Collections::IMap<Platform::String ^, double> ^ variables);
|
||||
|
||||
void SetSelectedEquation(EquationViewModel ^ equation);
|
||||
private:
|
||||
void OnButtonPressed(Platform::Object ^ parameter);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "GraphingSettingsViewModel.h"
|
||||
#include <CalcManager\NumberFormattingUtils.h>
|
||||
|
||||
using namespace CalculatorApp::ViewModel;
|
||||
using namespace CalcManager::NumberFormattingUtils;
|
||||
using namespace GraphControl;
|
||||
using namespace std;
|
||||
using namespace Platform;
|
||||
|
||||
GraphingSettingsViewModel::GraphingSettingsViewModel()
|
||||
: m_XMinValue(0)
|
||||
, m_XMaxValue(0)
|
||||
, m_YMinValue(0)
|
||||
, m_YMaxValue(0)
|
||||
, m_XMinError(false)
|
||||
, m_XMaxError(false)
|
||||
, m_YMinError(false)
|
||||
, m_YMaxError(false)
|
||||
, m_dontUpdateDisplayRange(false)
|
||||
, m_XIsMinLastChanged(true)
|
||||
, m_YIsMinLastChanged(true)
|
||||
{
|
||||
}
|
||||
|
||||
void GraphingSettingsViewModel::SetGrapher(Grapher ^ grapher)
|
||||
{
|
||||
if (grapher != nullptr)
|
||||
{
|
||||
if (grapher->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Invalid)
|
||||
{
|
||||
grapher->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians;
|
||||
}
|
||||
}
|
||||
Graph = grapher;
|
||||
InitRanges();
|
||||
RaisePropertyChanged(L"TrigUnit");
|
||||
}
|
||||
|
||||
void GraphingSettingsViewModel::InitRanges()
|
||||
{
|
||||
double xMin = 0, xMax = 0, yMin = 0, yMax = 0;
|
||||
if (m_Graph != nullptr)
|
||||
{
|
||||
m_Graph->GetDisplayRanges(&xMin, &xMax, &yMin, &yMax);
|
||||
}
|
||||
m_dontUpdateDisplayRange = true;
|
||||
m_XMinValue = xMin;
|
||||
m_XMaxValue = xMax;
|
||||
m_YMinValue = yMin;
|
||||
m_YMaxValue = yMax;
|
||||
auto valueStr = to_wstring(m_XMinValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_XMin = ref new String(valueStr.c_str());
|
||||
|
||||
valueStr = to_wstring(m_XMaxValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_XMax = ref new String(valueStr.c_str());
|
||||
|
||||
valueStr = to_wstring(m_YMinValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_YMin = ref new String(valueStr.c_str());
|
||||
|
||||
valueStr = to_wstring(m_YMaxValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_YMax = ref new String(valueStr.c_str());
|
||||
|
||||
m_dontUpdateDisplayRange = false;
|
||||
}
|
||||
|
||||
void GraphingSettingsViewModel::UpdateDisplayRange(bool XValuesModified)
|
||||
{
|
||||
if (m_Graph == nullptr || m_dontUpdateDisplayRange || HasError())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Graph->ForceProportionalAxes)
|
||||
{
|
||||
// If ForceProportionalAxes is set, the graph will try to automatically adjust ranges to remain proportional.
|
||||
// but without a logic to choose which values can be modified or not.
|
||||
// To solve this problem, we calculate the new ranges here, taking care to not modify the current axis and
|
||||
// modifying only the least recently updated value of the other axis.
|
||||
|
||||
if (XValuesModified)
|
||||
{
|
||||
if (m_YIsMinLastChanged)
|
||||
{
|
||||
auto yMaxValue = m_YMinValue + (m_XMaxValue - m_XMinValue) * m_Graph->ActualHeight / m_Graph->ActualWidth;
|
||||
if (m_YMaxValue != yMaxValue)
|
||||
{
|
||||
m_YMaxValue = yMaxValue;
|
||||
auto valueStr = to_wstring(m_YMaxValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_YMax = ref new String(valueStr.c_str());
|
||||
RaisePropertyChanged("YMax");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto yMinValue = m_YMaxValue - (m_XMaxValue - m_XMinValue) * m_Graph->ActualHeight / m_Graph->ActualWidth;
|
||||
if (m_YMinValue != yMinValue)
|
||||
{
|
||||
m_YMinValue = yMinValue;
|
||||
auto valueStr = to_wstring(m_YMinValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_YMin = ref new String(valueStr.c_str());
|
||||
RaisePropertyChanged("YMin");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_XIsMinLastChanged)
|
||||
{
|
||||
auto xMaxValue = m_XMinValue + (m_YMaxValue - m_YMinValue) * m_Graph->ActualWidth / m_Graph->ActualHeight;
|
||||
if (m_XMaxValue != xMaxValue)
|
||||
{
|
||||
m_XMaxValue = xMaxValue;
|
||||
auto valueStr = to_wstring(m_XMaxValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_XMax = ref new String(valueStr.c_str());
|
||||
RaisePropertyChanged("XMax");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto xMinValue = m_XMaxValue - (m_YMaxValue - m_YMinValue) * m_Graph->ActualWidth / m_Graph->ActualHeight;
|
||||
if (m_XMinValue != xMinValue)
|
||||
{
|
||||
m_XMinValue = xMinValue;
|
||||
auto valueStr = to_wstring(m_XMinValue);
|
||||
TrimTrailingZeros(valueStr);
|
||||
m_XMin = ref new String(valueStr.c_str());
|
||||
RaisePropertyChanged("XMin");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Graph->SetDisplayRanges(m_XMinValue, m_XMaxValue, m_YMinValue, m_YMaxValue);
|
||||
}
|
||||
|
||||
bool GraphingSettingsViewModel::HasError()
|
||||
{
|
||||
return m_XMinError || m_YMinError || m_XMaxError || m_YMaxError || XError || YError;
|
||||
}
|
293
src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h
Normal file
293
src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h
Normal file
|
@ -0,0 +1,293 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "../Common/Utils.h"
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
#pragma once
|
||||
[Windows::UI::Xaml::Data::Bindable] public ref class GraphingSettingsViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_R(bool, YMinError);
|
||||
OBSERVABLE_PROPERTY_R(bool, XMinError);
|
||||
OBSERVABLE_PROPERTY_R(bool, XMaxError);
|
||||
OBSERVABLE_PROPERTY_R(bool, YMaxError);
|
||||
OBSERVABLE_PROPERTY_R(GraphControl::Grapher ^, Graph);
|
||||
|
||||
GraphingSettingsViewModel();
|
||||
|
||||
property bool XError
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return !m_XMinError && !m_XMaxError && m_XMinValue >= m_XMaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
property bool YError
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return !m_YMinError && !m_YMaxError && m_YMinValue >= m_YMaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String ^ XMin
|
||||
{
|
||||
Platform::String ^ get()
|
||||
{
|
||||
return m_XMin;
|
||||
}
|
||||
void set(Platform::String ^ value)
|
||||
{
|
||||
if (m_XMin == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_XMin = value;
|
||||
m_XIsMinLastChanged = true;
|
||||
if (m_Graph != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t sz;
|
||||
auto number = std::stod(value->Data(), &sz);
|
||||
if (value->Length() == sz)
|
||||
{
|
||||
m_Graph->XAxisMin = m_XMinValue = number;
|
||||
XMinError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
XMinError = true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
XMinError = true;
|
||||
}
|
||||
}
|
||||
RaisePropertyChanged("XError");
|
||||
RaisePropertyChanged("XMin");
|
||||
UpdateDisplayRange(true);
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String ^ XMax
|
||||
{
|
||||
Platform::String ^ get()
|
||||
{
|
||||
return m_XMax;
|
||||
}
|
||||
void set(Platform::String ^ value)
|
||||
{
|
||||
if (m_XMax == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_XMax = value;
|
||||
m_XIsMinLastChanged = false;
|
||||
if (m_Graph != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t sz;
|
||||
auto number = std::stod(value->Data(), &sz);
|
||||
if (value->Length() == sz)
|
||||
{
|
||||
m_Graph->XAxisMax = m_XMaxValue = number;
|
||||
XMaxError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
XMaxError = true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
XMaxError = true;
|
||||
}
|
||||
}
|
||||
RaisePropertyChanged("XError");
|
||||
RaisePropertyChanged("XMax");
|
||||
UpdateDisplayRange(true);
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String ^ YMin
|
||||
{
|
||||
Platform::String ^ get()
|
||||
{
|
||||
return m_YMin;
|
||||
}
|
||||
void set(Platform::String ^ value)
|
||||
{
|
||||
if (m_YMin == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_YMin = value;
|
||||
m_YIsMinLastChanged = true;
|
||||
if (m_Graph != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t sz;
|
||||
auto number = std::stod(value->Data(), &sz);
|
||||
if (value->Length() == sz)
|
||||
{
|
||||
m_Graph->YAxisMin = m_YMinValue = number;
|
||||
YMinError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
YMinError = true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
YMinError = true;
|
||||
}
|
||||
}
|
||||
RaisePropertyChanged("YError");
|
||||
RaisePropertyChanged("YMin");
|
||||
UpdateDisplayRange(false);
|
||||
}
|
||||
}
|
||||
|
||||
property Platform::String ^ YMax
|
||||
{
|
||||
Platform::String ^ get()
|
||||
{
|
||||
return m_YMax;
|
||||
}
|
||||
void set(Platform::String ^ value)
|
||||
{
|
||||
if (m_YMax == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_YMax = value;
|
||||
m_YIsMinLastChanged = false;
|
||||
if (m_Graph != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t sz;
|
||||
auto number = std::stod(value->Data(), &sz);
|
||||
if (value->Length() == sz)
|
||||
{
|
||||
m_Graph->YAxisMax = m_YMaxValue = number;
|
||||
YMaxError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
YMaxError = true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
YMaxError = true;
|
||||
}
|
||||
}
|
||||
RaisePropertyChanged("YError");
|
||||
RaisePropertyChanged("YMax");
|
||||
UpdateDisplayRange(false);
|
||||
}
|
||||
}
|
||||
|
||||
property int TrigUnit
|
||||
{
|
||||
int get()
|
||||
{
|
||||
return m_Graph == nullptr ? (int)Graphing::EvalTrigUnitMode::Invalid : m_Graph->TrigUnitMode;
|
||||
}
|
||||
void set(int value)
|
||||
{
|
||||
if (m_Graph == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Graph->TrigUnitMode = value;
|
||||
RaisePropertyChanged(L"TrigUnit");
|
||||
}
|
||||
}
|
||||
|
||||
property bool TrigModeRadians
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Radians;
|
||||
}
|
||||
void set(bool value)
|
||||
{
|
||||
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Radians)
|
||||
{
|
||||
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians;
|
||||
RaisePropertyChanged(L"TrigModeRadians");
|
||||
RaisePropertyChanged(L"TrigModeDegrees");
|
||||
RaisePropertyChanged(L"TrigModeGradians");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool TrigModeDegrees
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Degrees;
|
||||
}
|
||||
void set(bool value)
|
||||
{
|
||||
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Degrees)
|
||||
{
|
||||
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Degrees;
|
||||
RaisePropertyChanged(L"TrigModeDegrees");
|
||||
RaisePropertyChanged(L"TrigModeRadians");
|
||||
RaisePropertyChanged(L"TrigModeGradians");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property bool TrigModeGradians
|
||||
{
|
||||
bool get()
|
||||
{
|
||||
return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Grads;
|
||||
}
|
||||
void set(bool value)
|
||||
{
|
||||
if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Grads)
|
||||
{
|
||||
m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Grads;
|
||||
RaisePropertyChanged(L"TrigModeGradians");
|
||||
RaisePropertyChanged(L"TrigModeDegrees");
|
||||
RaisePropertyChanged(L"TrigModeRadians");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void UpdateDisplayRange(bool XValuesModified);
|
||||
|
||||
public:
|
||||
void SetGrapher(GraphControl::Grapher ^ grapher);
|
||||
void InitRanges();
|
||||
bool HasError();
|
||||
|
||||
private:
|
||||
Platform::String ^ m_XMin;
|
||||
Platform::String ^ m_XMax;
|
||||
Platform::String ^ m_YMin;
|
||||
Platform::String ^ m_YMax;
|
||||
double m_XMinValue;
|
||||
double m_XMaxValue;
|
||||
double m_YMinValue;
|
||||
double m_YMaxValue;
|
||||
bool m_dontUpdateDisplayRange;
|
||||
bool m_XIsMinLastChanged;
|
||||
bool m_YIsMinLastChanged;
|
||||
};
|
||||
}
|
69
src/CalcViewModel/GraphingCalculator/VariableViewModel.h
Normal file
69
src/CalcViewModel/GraphingCalculator/VariableViewModel.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Common/Utils.h"
|
||||
#include "EquationViewModel.h"
|
||||
|
||||
namespace CalculatorApp::ViewModel
|
||||
{
|
||||
public
|
||||
value struct VariableChangedEventArgs sealed
|
||||
{
|
||||
Platform::String ^ variableName;
|
||||
double newValue;
|
||||
};
|
||||
|
||||
[Windows::UI::Xaml::Data::Bindable] public ref class VariableViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged
|
||||
{
|
||||
public:
|
||||
VariableViewModel(Platform::String ^ name, double value)
|
||||
: m_Name(name)
|
||||
, m_Value(value)
|
||||
, m_SliderSettingsVisible(false)
|
||||
, m_Min(0.0)
|
||||
, m_Step(0.1)
|
||||
, m_Max(2.0)
|
||||
{
|
||||
}
|
||||
|
||||
OBSERVABLE_OBJECT();
|
||||
OBSERVABLE_PROPERTY_R(Platform::String ^, Name);
|
||||
OBSERVABLE_PROPERTY_RW(double, Min);
|
||||
OBSERVABLE_PROPERTY_RW(double, Step);
|
||||
OBSERVABLE_PROPERTY_RW(double, Max);
|
||||
OBSERVABLE_PROPERTY_RW(bool, SliderSettingsVisible);
|
||||
|
||||
event Windows::Foundation::EventHandler<VariableChangedEventArgs> ^ VariableUpdated;
|
||||
|
||||
property double Value
|
||||
{
|
||||
double get()
|
||||
{
|
||||
return m_Value;
|
||||
}
|
||||
void set(double value)
|
||||
{
|
||||
if (value < Min)
|
||||
{
|
||||
value = Min;
|
||||
}
|
||||
else if (value > Max)
|
||||
{
|
||||
value = Max;
|
||||
}
|
||||
|
||||
if (Value != value)
|
||||
{
|
||||
m_Value = value;
|
||||
VariableUpdated(this, VariableChangedEventArgs{ Name, value });
|
||||
RaisePropertyChanged(L"Value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double m_Value;
|
||||
};
|
||||
}
|
28
src/CalcViewModel/GraphingCalculatorEnums.h
Normal file
28
src/CalcViewModel/GraphingCalculatorEnums.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
enum KeyGraphFeaturesFlag
|
||||
{
|
||||
Domain = 1,
|
||||
Range = 2,
|
||||
Parity = 4,
|
||||
Periodicity = 8,
|
||||
Zeros = 16,
|
||||
YIntercept = 32,
|
||||
Minima = 64,
|
||||
Maxima = 128,
|
||||
InflectionPoints = 256,
|
||||
VerticalAsymptotes = 512,
|
||||
HorizontalAsymptotes = 1024,
|
||||
ObliqueAsymptotes = 2048,
|
||||
MonotoneIntervals = 4096
|
||||
};
|
||||
|
||||
enum AnalysisErrorType
|
||||
{
|
||||
NoError,
|
||||
AnalysisCouldNotBePerformed,
|
||||
AnalysisNotSupported
|
||||
};
|
||||
}
|
19
src/CalcViewModel/ViewState.cpp
Normal file
19
src/CalcViewModel/ViewState.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "pch.h"
|
||||
#include "ViewState.h"
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewState
|
||||
{
|
||||
Platform::StringReference Snap(L"Snap");
|
||||
Platform::StringReference DockedView(L"DockedView");
|
||||
|
||||
bool IsValidViewState(Platform::String ^ viewState)
|
||||
{
|
||||
return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView);
|
||||
}
|
||||
}
|
||||
}
|
15
src/CalcViewModel/ViewState.h
Normal file
15
src/CalcViewModel/ViewState.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CalculatorApp
|
||||
{
|
||||
namespace ViewState
|
||||
{
|
||||
extern Platform::StringReference Snap;
|
||||
extern Platform::StringReference DockedView;
|
||||
|
||||
bool IsValidViewState(Platform::String ^ viewState);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue