Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/GraphingCalculator initial commit #450

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -289,6 +289,7 @@ __pycache__/

# Calculator specific
Generated Files/
src/GraphControl/GraphingImplOverrides.props
!/build/config/TRexDefs/**
!src/Calculator/TemporaryKey.pfx
!src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -45,6 +45,10 @@ We also welcome [issues submitted on GitHub](https://github.com/Microsoft/calcul
## Roadmap
For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md).

### Graphing Mode
Adding graphing calculator functionality [is on the project roadmap](https://github.com/Microsoft/calculator/issues/338) and we hope that this project can create a great end-user experience around graphing. To that end, the UI from the official in-box Windows Calculator is currently part of this repository, although the proprietary Microsoft-built graphing engine, which also drives graphing in Microsoft Mathematics and OneNote, is not. Community members can still be involved in the creation of the UI, however developer builds will not have graphing functionality due to the use of a [mock implementation of the engine](/src/MockGraphingImpl) built on top of a
[common graphing API](/src/GraphingInterfaces).

## Data / Telemetry
This project collects usage data and sends it to Microsoft to help improve our products and services.
Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkId=521839) to learn more.
Expand Down
10 changes: 9 additions & 1 deletion src/CalcViewModel/ApplicationViewModel.cpp
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "pch.h"
Expand Down Expand Up @@ -42,6 +42,7 @@ namespace
ApplicationViewModel::ApplicationViewModel() :
m_CalculatorViewModel(nullptr),
m_DateCalcViewModel(nullptr),
m_GraphingCalcViewModel(nullptr),
m_ConverterViewModel(nullptr),
m_PreviousMode(ViewMode::None),
m_mode(ViewMode::None),
Expand Down Expand Up @@ -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))
{
TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()));
Expand Down
2 changes: 2 additions & 0 deletions src/CalcViewModel/ApplicationViewModel.h
Expand Up @@ -5,6 +5,7 @@

#include "StandardCalculatorViewModel.h"
#include "DateCalculatorViewModel.h"
#include "GraphingCalculator/GraphingCalculatorViewModel.h"
#include "UnitConverterViewModel.h"

namespace CalculatorApp
Expand All @@ -22,6 +23,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_NAMED_PROPERTY_RW(Platform::String^, CategoryName);
Expand Down
4 changes: 4 additions & 0 deletions src/CalcViewModel/CalcViewModel.vcxproj
Expand Up @@ -350,6 +350,8 @@
<ClInclude Include="DataLoaders\UnitConverterDataConstants.h" />
<ClInclude Include="DataLoaders\UnitConverterDataLoader.h" />
<ClInclude Include="DateCalculatorViewModel.h" />
<ClInclude Include="GraphingCalculator\EquationViewModel.h" />
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h" />
<ClInclude Include="HistoryItemViewModel.h" />
<ClInclude Include="HistoryViewModel.h" />
<ClInclude Include="MemoryItemViewModel.h" />
Expand Down Expand Up @@ -386,6 +388,8 @@
<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="HistoryItemViewModel.cpp" />
<ClCompile Include="HistoryViewModel.cpp" />
<ClCompile Include="MemoryItemViewModel.cpp" />
Expand Down
15 changes: 15 additions & 0 deletions src/CalcViewModel/CalcViewModel.vcxproj.filters
Expand Up @@ -10,6 +10,9 @@
<Filter Include="DataLoaders">
<UniqueIdentifier>{0184f727-b8aa-4af8-a699-63f1b56e7853}</UniqueIdentifier>
</Filter>
<Filter Include="GraphingCalculator">
<UniqueIdentifier>{f7519cec-2ebd-432b-9d59-9647de131d50}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp" />
Expand Down Expand Up @@ -93,6 +96,12 @@
<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>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
Expand Down Expand Up @@ -213,6 +222,12 @@
<ClInclude Include="Common\TraceActivity.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\EquationViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
<ClInclude Include="GraphingCalculator\GraphingCalculatorViewModel.h">
<Filter>GraphingCalculator</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="DataLoaders\DefaultFromToCurrency.json">
Expand Down
24 changes: 14 additions & 10 deletions src/CalcViewModel/Common/CalculatorButtonUser.h
Expand Up @@ -62,10 +62,6 @@ namespace CalculatorApp
IsStandardMode = (int) CM::Command::ModeBasic,
None = (int) CM::Command::CommandNULL,
IsProgrammerMode = (int) CM::Command::ModeProgrammer,
DecButton = (int) CM::Command::CommandDec,
OctButton = (int) CM::Command::CommandOct,
HexButton = (int) CM::Command::CommandHex,
BinButton = (int) CM::Command::CommandBin,
And = (int) CM::Command::CommandAnd,
Ror = (int) CM::Command::CommandROR,
Rol = (int) CM::Command::CommandROL,
Expand All @@ -87,12 +83,21 @@ namespace CalculatorApp
InvSinh = (int) CM::Command::CommandASINH,
InvCosh = (int) CM::Command::CommandACOSH,
InvTanh = (int) CM::Command::CommandATANH,
Qword = (int) CM::Command::CommandQword,
Dword = (int) CM::Command::CommandDword,
Word = (int) CM::Command::CommandWord,
Byte = (int) CM::Command::CommandByte,
Cube = (int) CM::Command::CommandCUB,
DMS = (int) CM::Command::CommandDMS,
Hyp = (int)CM::Command::CommandHYP,
HexButton = (int)CM::Command::CommandHex,
DecButton = (int)CM::Command::CommandDec,
OctButton = (int)CM::Command::CommandOct,
BinButton = (int)CM::Command::CommandBin,
Qword = (int)CM::Command::CommandQword,
Dword = (int)CM::Command::CommandDword,
Word = (int)CM::Command::CommandWord,
Byte = (int)CM::Command::CommandByte,

Plot,
X,
Y,

BINSTART = (int) CM::Command::CommandBINEDITSTART,
BINPOS0 = (int) CM::Command::CommandBINPOS0,
Expand Down Expand Up @@ -159,8 +164,7 @@ namespace CalculatorApp
BINPOS61 = (int) CM::Command::CommandBINPOS61,
BINPOS62 = (int) CM::Command::CommandBINPOS62,
BINPOS63 = (int) CM::Command::CommandBINPOS63,
BINEND = (int) CM::Command::CommandBINEDITEND,
Hyp = (int) CM::Command::CommandHYP
BINEND = (int) CM::Command::CommandBINEDITEND
};

// This contains list of functions whose usage we are tracelogging
Expand Down
44 changes: 13 additions & 31 deletions src/CalcViewModel/Common/KeyboardShortcutManager.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -70,7 +71,7 @@ namespace CalculatorApp
}
}

void LightUpButton(ButtonBase^ button)
winrt::fire_and_forget LightUpButton(ButtonBase^ button)
{
// If the button is a toggle button then we don't need
// to change the UI of the button
Expand All @@ -82,33 +83,15 @@ namespace CalculatorApp
// The button will go into the visual Pressed state with this call
VisualStateManager::GoToState(button, "Pressed", true);

// This timer will fire after lightUpTime and make the button
// go back to the normal state.
// This timer will only fire once after which it will be destroyed
auto timer = ref new DispatcherTimer();
TimeSpan lightUpTime{};
lightUpTime.Duration = 500000L; // Half second (in 100-ns units)
timer->Interval = lightUpTime;

WeakReference timerWeakReference(timer);
WeakReference buttonWeakReference(button);
timer->Tick += ref new EventHandler<Object^>(
[buttonWeakReference, timerWeakReference](Object^, Object^)
{
auto button = buttonWeakReference.Resolve<ButtonBase>();
if (button)
{
VisualStateManager::GoToState(button, "Normal", true);
}
winrt::apartment_context uiThreadContext;

// Cancel the timer after we're done so it only fires once
auto timer = timerWeakReference.Resolve<DispatcherTimer>();
if (timer)
{
timer->Stop();
}
});
timer->Start();
co_await winrt::resume_background();
co_await winrt::resume_after(500ms);

co_await uiThreadContext;

// Restore the normal state
VisualStateManager::GoToState(button, "Normal", true);
}

// Looks for the first button reference that it can resolve
Expand Down Expand Up @@ -457,9 +440,8 @@ void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow^ sender, Cha
wchar_t character = static_cast<wchar_t>(args->KeyCode);
auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character);

RunFirstEnabledButtonCommand(buttons);

LightUpButtons(buttons);
RunFirstEnabledButtonCommand(buttons);
}
}
}
Expand Down Expand Up @@ -613,8 +595,6 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
{
if (currentHonorShortcuts->second)
{
RunFirstEnabledButtonCommand(buttons);

// Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V is pressed.
// When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open.
// Ctrl+Insert is equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V
Expand All @@ -627,6 +607,8 @@ void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow^ sender, KeyEventArgs^
LightUpButtons(buttons);
}
}

RunFirstEnabledButtonCommand(buttons);
}
}
}
Expand Down
16 changes: 12 additions & 4 deletions src/CalcViewModel/Common/NavCategory.cpp
Expand Up @@ -46,14 +46,16 @@ 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 ^^^

// The order of items in this list determines the order of items in the menu.
static constexpr array<const NavCategoryInitializer, 17> s_categoryManifest = {
static constexpr array<const NavCategoryInitializer, 18> s_categoryManifest = {
NavCategoryInitializer { ViewMode::Standard, STANDARD_ID, L"Standard", L"StandardMode", L"\uE8EF", CategoryGroupType::Calculator, MyVirtualKey::Number1, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Scientific, SCIENTIFIC_ID, L"Scientific", L"ScientificMode", L"\uF196", CategoryGroupType::Calculator, MyVirtualKey::Number2, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Programmer, PROGRAMMER_ID, L"Programmer", L"ProgrammerMode", L"\uECCE", CategoryGroupType::Calculator, MyVirtualKey::Number3, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Date, DATE_ID, L"Date", L"DateCalculationMode", L"\uE787", CategoryGroupType::Calculator, MyVirtualKey::Number4, SUPPORTS_ALL },
NavCategoryInitializer { ViewMode::Graphing, GRAPHING_ID, L"Graphing", L"GraphingCalculatorMode", L"\uF770", CategoryGroupType::Calculator, MyVirtualKey::Number5, 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 },
Expand Down Expand Up @@ -121,9 +123,15 @@ 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)
Expand Down
4 changes: 3 additions & 1 deletion src/CalcViewModel/Common/NavCategory.h
Expand Up @@ -43,7 +43,8 @@ namespace CalculatorApp
Data = 13,
Pressure = 14,
Angle = 15,
Currency = 16
Currency = 16,
Graphing = 17
};

public enum class CategoryGroupType
Expand Down Expand Up @@ -163,6 +164,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);

Expand Down
56 changes: 54 additions & 2 deletions src/CalcViewModel/Common/Utils.cpp
Expand Up @@ -15,11 +15,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;
Expand Down Expand Up @@ -68,7 +70,7 @@ int Utils::GetWindowId()
return windowId;
}

void Utils::RunOnUIThreadNonblocking(std::function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
void Utils::RunOnUIThreadNonblocking(function<void()>&& function, _In_ CoreDispatcher^ currentDispatcher)
{
if (currentDispatcher != nullptr)
{
Expand All @@ -91,7 +93,7 @@ wstring Utils::RemoveUnwantedCharsFromWstring(wstring input, wchar_t* unwantedCh
{
for (unsigned int i = 0; i < size; ++i)
{
input.erase(std::remove(input.begin(), input.end(), unwantedChars[i]), input.end());
input.erase(remove(input.begin(), input.end(), unwantedChars[i]), input.end());
}
return input;
}
Expand Down Expand Up @@ -225,3 +227,53 @@ task<String^> Utils::ReadFileFromFolder(IStorageFolder^ folder, String^ fileName
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());
}

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);
}