diff --git a/README.md b/README.md index 773a27ef4..085fae6b0 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,9 @@ Prerequisites: - Open [src\Calculator.sln](/src/Calculator.sln) in Visual Studio to build and run the Calculator app. - For a general description of the Calculator project architecture see [ApplicationArchitecture.md](docs/ApplicationArchitecture.md). +- To run the UI Tests, you need to make sure that + [Windows Application Driver (WinAppDriver)](https://github.com/microsoft/WinAppDriver/releases/latest) + is installed. ## Contributing Want to contribute? The team encourages community feedback and contributions. Please follow our [contributing guidelines](CONTRIBUTING.md). @@ -52,25 +55,20 @@ For information regarding Windows Calculator plans and release schedule, please 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 +## Diagnostic Data 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. -Telemetry is disabled in development builds by default, and can be enabled with the `SEND_TELEMETRY` +Diagnostic data is disabled in development builds by default, and can be enabled with the `SEND_DIAGNOSTICS` build flag. ## Currency Converter -Windows Calculator includes a currency converter feature that uses mock data in developer builds. The data that -Microsoft uses for the currency converter feature (e.g., in the retail version of the application) is not licensed -for your use. The mock data will be clearly identifiable as it references planets instead of countries, +Windows Calculator includes a currency converter feature that uses mock data in developer builds. The data that +Microsoft uses for the currency converter feature (e.g., in the retail version of the application) is not licensed +for your use. The mock data will be clearly identifiable as it references planets instead of countries, and remains static regardless of selected inputs. ## Reporting Security Issues -Security issues and bugs should be reported privately, via email, to the -Microsoft Security Response Center (MSRC) at <[secure@microsoft.com](mailto:secure@microsoft.com)>. -You should receive a response within 24 hours. If for some reason you do not, please follow up via -email to ensure we received your original message. Further information, including the -[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the -[Security TechCenter](https://technet.microsoft.com/en-us/security/default). +Please refer to [SECURITY.md](./SECURITY.md). ## License Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..1b724719f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,48 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all +source code repositories managed through our GitHub organizations, which include +[Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), +[DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), +[Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets +Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) +of a security vulnerability, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** Instead, please +report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). +If possible, encrypt your message with our PGP key; please download it from the +[Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via +email to ensure we received your original message. Additional information can be found at +[microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better +understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of +[Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + diff --git a/build/pipelines/azure-pipelines.release.yaml b/build/pipelines/azure-pipelines.release.yaml index 0694fedc8..c26df788d 100644 --- a/build/pipelines/azure-pipelines.release.yaml +++ b/build/pipelines/azure-pipelines.release.yaml @@ -9,8 +9,8 @@ pr: none variables: versionMajor: 10 - versionMinor: 1906 - versionBuild: $[counter('10.1906.*', 0)] + versionMinor: 1907 + versionBuild: $[counter('10.1907.*', 0)] versionPatch: 0 name: '$(versionMajor).$(versionMinor).$(versionBuild).$(versionPatch)' diff --git a/build/pipelines/templates/build-app-internal.yaml b/build/pipelines/templates/build-app-internal.yaml index 9dc0d4219..8aa5f6751 100644 --- a/build/pipelines/templates/build-app-internal.yaml +++ b/build/pipelines/templates/build-app-internal.yaml @@ -29,7 +29,7 @@ jobs: downloadDirectory: $(Build.SourcesDirectory) vstsFeed: WindowsApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.11 + vstsPackageVersion: 0.0.18 - template: ./build-single-architecture.yaml parameters: diff --git a/build/pipelines/templates/package-appxbundle.yaml b/build/pipelines/templates/package-appxbundle.yaml index f88f155f7..1e06d20d0 100644 --- a/build/pipelines/templates/package-appxbundle.yaml +++ b/build/pipelines/templates/package-appxbundle.yaml @@ -38,10 +38,13 @@ jobs: filePath: $(Build.SourcesDirectory)\build\scripts\CreateAppxBundleMapping.ps1 arguments: '-InputPath $(Build.ArtifactStagingDirectory)\drop\Release -ProjectName Calculator -OutputFile $(Build.BinariesDirectory)\AppxBundleMapping.txt' - - script: '"C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x86\MakeAppx.exe" bundle /v /bv %BUNDLEVERSION% /f %MAPPINGFILEPATH% /p %OUTPUTPATH%' + - powershell: | + $buildVersion = [version]$Env:BUILDVERSION + $bundleVersion = "2020.$($buildVersion.Minor).$($buildVersion.Build).$($buildVersion.Revision)" + & "C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86\MakeAppx.exe" bundle /v /bv $bundleVersion /f $Env:MAPPINGFILEPATH /p $Env:OUTPUTPATH displayName: Make AppxBundle env: - BUNDLEVERSION: $(Build.BuildNumber) + BUILDVERSION: $(Build.BuildNumber) MAPPINGFILEPATH: $(Build.BinariesDirectory)\AppxBundleMapping.txt OUTPUTPATH: $(Build.BinariesDirectory)\Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle diff --git a/build/pipelines/templates/prepare-release-internalonly.yaml b/build/pipelines/templates/prepare-release-internalonly.yaml index a6b43a1b7..38d0f3076 100644 --- a/build/pipelines/templates/prepare-release-internalonly.yaml +++ b/build/pipelines/templates/prepare-release-internalonly.yaml @@ -91,7 +91,7 @@ jobs: downloadDirectory: $(Build.SourcesDirectory) vstsFeed: WindowsApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.11 + vstsPackageVersion: 0.0.18 - task: PkgESStoreBrokerPackage@10 displayName: Create StoreBroker Packages diff --git a/docs/ManualTests.md b/docs/ManualTests.md index a1d9de587..ddc38ac14 100644 --- a/docs/ManualTests.md +++ b/docs/ManualTests.md @@ -54,6 +54,59 @@ Steps: 2. Select “miles” as the unit type in the output field *Expected: The output starts with is “3.106856”* +### Always-on-Top + +**Test 1** +Steps: +1. Launch the "Calculator" app and navigate to "Standard" Calculator +*Expected: Always-on-Top button's tooltip says "Keep on top"* +2. Click the Always-on-Top button +*Expected: Always-on-Top button's tooltip now says "Back to full view"* +3. Launch the "Notepad" app and put it in full-screen mode +*Expected: Calculator is still on top of Notepad and in Always-on-Top mode* + +**Test 2** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, input “3”, “+”, “3” (do not press “Enter”) +2. Tab over the Always-on-Top button and press "Enter" on the keyboard +*Expected: The application title, hamburger menu, calculator type title, calculation expression (the secondary line above the main display), history button and memory buttons are no longer visible. The main display shows "3"* +2. Press “Enter” +*Expected: The main display shows "6"* +3. Press "Ctrl-H" on the keyboard +*Expected: Nothing happens (history keyboard shortcuts are disabled)* +4. Press "Ctrl-P" on the keyboard, then tab over the Always-on-Top button and press "Enter" on the keyboard again +5. Open the Memory panel +*Expected: Nothing is stored in memory (memory keyboard shortcuts are disabled in Always-on-Top mode) and "6" is in history* + +**Test 3** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +2. Resize the window horizontally +*Expected: The buttons automatically expand or shrink to fit the available screen size* +3. Resize the window vertically +*Expected: The buttons automatically expand or shrink to fit the available screen size and the percent, square-root, squared and reciprocal buttons disappear when the screen height is small* +4. Click the Always-on-Top button again +*Expected: Calculator is in Standard mode and the original window layout from before Step 1 is restored* +5. Click the Always-on-Top button again +*Expected: Calculator is in Always-on-Top mode and the window size from after Step 3 is restored* +6. Close the "Calculator" app +7. Launch the "Calculator" app again and click the Always-on-Top button +*Expected: The window size from right before closing from Always-on-Top mode (ie. after Step 5) is restored* + +**Test 4** +Steps: +1. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +2. Input "/", "0", “Enter” on the keyboard +*Expected: "Result is undefined" is displayed in the system default app language* +3. Click the Always-on-Top button again +*Expected: Calculator is in Standard mode and all operator (except for "CE", "C", "Delete" and "=") and memory buttons are disabled + +**Test 5** +Steps: +1. Launch the "Calculator" app and navigate to "Scientific" Calculator +*Expected: The Always-on-Top button is hidden* +2. Navigate to "Standard" Calculator +*Expected: The Always-on-Top button is visible* ## Basic Verification Tests @@ -278,7 +331,7 @@ Steps: Steps: 1. Launch the "Calculator" app. - For All Applicable Modes verify the following: + For All Applicable Modes verify the following (note: only 11-15 and 20 work in Always-on-Top mode): 2. Press **Alt +1** to Enter "Standard" mode *Expected: Move to "Standard" screen.* 3. Press **Alt +2** to Enter "Scientific" mode @@ -353,3 +406,30 @@ Steps: 61. Press **|** to Select 'Or' 62. Press **~** to Select 'Not' 63. Press **&** to Select 'And' + +## Localization Tests + +### Always-on-Top + +**Test 1** +Steps: +1. Change the system default app language to Arabic +2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +*Expected: UI/Menu is localized (for example, the title bar buttons is in right-to-left order)* +3. Input "/", "0", “Enter” on the keyboard +*Expected: Error message is in Arabic* + +## Ease of Access Tests + +### Always-on-Top + +**Test 1** +Steps: +1. Open the "Narrator" app +2. Launch the "Calculator" app and from "Standard" Calculator, click the Always-on-Top button +3. Tab over the Always-on-Top button +*Expected: Narrator reads the localized version of "Back to full view"* +4. Tab over the main results field +*Expected: Narrator reads the localized version of exactly what's displayed (ie. "0")* +5. Tab over the rest of the UI elements +*Expected: Narrator reads the localized version of the UI elements' contents* diff --git a/src/CalcManager/CEngine/scicomm.cpp b/src/CalcManager/CEngine/scicomm.cpp index 20790f881..4752ec951 100644 --- a/src/CalcManager/CEngine/scicomm.cpp +++ b/src/CalcManager/CEngine/scicomm.cpp @@ -951,7 +951,7 @@ int CCalcEngine::GetCurrentRadix() return m_radix; } -wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision) +wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { Rational rat = (m_bRecord ? m_input.ToRational(m_radix, m_precision) : m_currentVal); @@ -964,7 +964,14 @@ wstring CCalcEngine::GetCurrentResultForRadix(uint32_t radix, int32_t precision) ChangeConstants(m_radix, m_precision); } - return GroupDigitsPerRadix(numberString, radix); + if (groupDigitsPerRadix) + { + return GroupDigitsPerRadix(numberString, radix); + } + else + { + return numberString; + } } wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix) diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index b83f27b6e..7022cd3e4 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -45,8 +45,8 @@ true Windows Store 10.0 - 10.0 - 10.0.18362.0 + 10.0.18362.0 + 10.0.17134.0 @@ -309,6 +309,7 @@ + @@ -349,6 +350,7 @@ Create Create + diff --git a/src/CalcManager/CalcManager.vcxproj.filters b/src/CalcManager/CalcManager.vcxproj.filters index 2ca116668..530989178 100644 --- a/src/CalcManager/CalcManager.vcxproj.filters +++ b/src/CalcManager/CalcManager.vcxproj.filters @@ -89,6 +89,7 @@ CEngine + @@ -160,5 +161,6 @@ Header Files + - + \ No newline at end of file diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index e09d7d909..71eebd222 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -37,15 +37,6 @@ namespace CalculationManager CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider); } - /// - /// Destructor for CalculatorManager - /// Ends two CCalcEngine - /// - CalculatorManager::~CalculatorManager() - { - this->MemorizedNumberClearAll(); - } - /// /// Call the callback function using passed in IDisplayHelper. /// Used to set the primary display value on ViewModel @@ -622,9 +613,9 @@ namespace CalculationManager } } - wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision) + wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { - return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision) : L""; + return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L""; } void CalculatorManager::SetPrecision(int32_t precision) diff --git a/src/CalcManager/CalculatorManager.h b/src/CalcManager/CalculatorManager.h index 37406ed47..358c2daf5 100644 --- a/src/CalcManager/CalculatorManager.h +++ b/src/CalcManager/CalculatorManager.h @@ -103,7 +103,6 @@ namespace CalculationManager void MemoryItemChanged(unsigned int indexOfMemory) override; CalculatorManager(ICalcDisplay* displayCallback, IResourceProvider* resourceProvider); - ~CalculatorManager(); void Reset(bool clearMemory = true); void SetStandardMode(); @@ -125,7 +124,7 @@ namespace CalculationManager } void SetRadix(RADIX_TYPE iRadixType); void SetMemorizedNumbersString(); - std::wstring GetResultForRadix(uint32_t radix, int32_t precision); + std::wstring GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); void SetPrecision(int32_t precision); void UpdateMaxIntDigits(); wchar_t DecimalSeparator(); diff --git a/src/CalcManager/ExpressionCommand.cpp b/src/CalcManager/ExpressionCommand.cpp index 079e02aa4..2d0547c64 100644 --- a/src/CalcManager/ExpressionCommand.cpp +++ b/src/CalcManager/ExpressionCommand.cpp @@ -137,6 +137,7 @@ void COpndCommand::AppendCommand(int command) { m_commands->Append(command); } + if (command == IDC_PNT) { m_fDecimal = true; @@ -256,38 +257,30 @@ const wstring& COpndCommand::GetToken(wchar_t decimalSymbol) } // Remove zeros - bool fDigitsFound = false; - int trimIdx = 0; for (unsigned int i = 0; i < m_token.size(); i++) { if (m_token.at(i) != chZero) { if (m_token.at(i) == decimalSymbol) { - trimIdx = i - 1; + m_token.erase(0, i - 1); } else { - trimIdx = i; + m_token.erase(0, i); } - fDigitsFound = true; - break; - } - } - if (fDigitsFound) - { - m_token.erase(0, trimIdx); - if (m_fNegative) - { - m_token.insert(0, &chNegate); + if (m_fNegative) + { + m_token.insert(0, &chNegate); + } + + return m_token; } } - else - { - m_token.clear(); - m_token.append(&chZero); - } + + m_token.clear(); + m_token.append(&chZero); return m_token; } diff --git a/src/CalcManager/Header Files/CalcEngine.h b/src/CalcManager/Header Files/CalcEngine.h index 9da576277..24441857c 100644 --- a/src/CalcManager/Header Files/CalcEngine.h +++ b/src/CalcManager/Header Files/CalcEngine.h @@ -75,7 +75,7 @@ class CCalcEngine void SettingsChanged(); bool IsCurrentTooBigForTrig(); int GetCurrentRadix(); - std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision); + std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); void ChangePrecision(int32_t precision) { m_precision = precision; diff --git a/src/CalcManager/NumberFormattingUtils.cpp b/src/CalcManager/NumberFormattingUtils.cpp new file mode 100644 index 000000000..8fde3d9a6 --- /dev/null +++ b/src/CalcManager/NumberFormattingUtils.cpp @@ -0,0 +1,84 @@ +#include "pch.h" +#include "NumberFormattingUtils.h" + +using namespace std; + +namespace CalcManager::NumberFormattingUtils +{ + /// + /// Trims out any trailing zeros or decimals in the given input string + /// + /// number to trim + void TrimTrailingZeros(_Inout_ wstring& number) + { + if (number.find(L'.') == wstring::npos) + { + return; + } + + wstring::iterator iter; + for (iter = number.end() - 1;; iter--) + { + if (*iter != L'0') + { + number.erase(iter + 1, number.end()); + break; + } + } + if (*(number.end() - 1) == L'.') + { + number.erase(number.end() - 1, number.end()); + } + } + + /// + /// Get number of digits (whole number part + decimal part) + /// the number + unsigned int GetNumberDigits(wstring value) + { + TrimTrailingZeros(value); + unsigned int numberSignificantDigits = static_cast(value.size()); + if (value.find(L'.') != wstring::npos) + { + --numberSignificantDigits; + } + if (value.find(L'-') != wstring::npos) + { + --numberSignificantDigits; + } + return numberSignificantDigits; + } + + /// + /// Get number of digits (whole number part only) + /// the number + unsigned int GetNumberDigitsWholeNumberPart(double value) + { + return value == 0 ? 1 : (1 + (int)log10(abs(value))); + } + + /// + /// Rounds the given double to the given number of significant digits + /// + /// input double + /// int number of significant digits to round to + wstring RoundSignificantDigits(double num, int numSignificant) + { + wstringstream out(wstringstream::out); + out << fixed; + out.precision(numSignificant); + out << num; + return out.str(); + } + + /// + /// Convert a Number to Scientific Notation + /// + /// number to convert + wstring ToScientificNumber(double number) + { + wstringstream out(wstringstream::out); + out << scientific << number; + return out.str(); + } +} diff --git a/src/CalcManager/NumberFormattingUtils.h b/src/CalcManager/NumberFormattingUtils.h new file mode 100644 index 000000000..ab337eedb --- /dev/null +++ b/src/CalcManager/NumberFormattingUtils.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include + +namespace CalcManager::NumberFormattingUtils +{ + void TrimTrailingZeros(_Inout_ std::wstring& input); + unsigned int GetNumberDigits(std::wstring value); + unsigned int GetNumberDigitsWholeNumberPart(double value); + std::wstring RoundSignificantDigits(double value, int numberSignificantDigits); + std::wstring ToScientificNumber(double number); +} diff --git a/src/CalcManager/UnitConverter.cpp b/src/CalcManager/UnitConverter.cpp index bbf761ba6..eb361fa51 100644 --- a/src/CalcManager/UnitConverter.cpp +++ b/src/CalcManager/UnitConverter.cpp @@ -7,9 +7,11 @@ #include // for std::sort #include "Command.h" #include "UnitConverter.h" +#include "NumberFormattingUtils.h" using namespace std; using namespace UnitConversionManager; +using namespace CalcManager::NumberFormattingUtils; static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3; static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6; @@ -178,7 +180,7 @@ void UnitConverter::SwitchActive(const wstring& newValue) swap(m_currentHasDecimal, m_returnHasDecimal); m_returnDisplay = m_currentDisplay; m_currentDisplay = newValue; - m_currentHasDecimal = (m_currentDisplay.find(L'.') != m_currentDisplay.npos); + m_currentHasDecimal = (m_currentDisplay.find(L'.') != wstring::npos); m_switchedActive = true; if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr) @@ -202,7 +204,7 @@ vector UnitConverter::StringToVector(const wstring& w, const wchar_t* d size_t delimiterIndex = w.find(delimiter); size_t startIndex = 0; vector serializedTokens = vector(); - while (delimiterIndex != w.npos) + while (delimiterIndex != wstring::npos) { serializedTokens.push_back(w.substr(startIndex, delimiterIndex - startIndex)); startIndex = delimiterIndex + (int)wcslen(delimiter); @@ -634,19 +636,19 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificant(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificant(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1); } else { - roundedString = RoundSignificant(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0); } if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative) { - TrimString(roundedString); + TrimTrailingZeros(roundedString); returnVector.push_back(make_tuple(roundedString, entry.type)); } } @@ -672,21 +674,21 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificant(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificant(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1); } else { - roundedString = RoundSignificant(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0); } // How to work out which is the best whimsical value to add to the vector? if (stod(roundedString) != 0.0) { - TrimString(roundedString); + TrimTrailingZeros(roundedString); whimsicalReturnVector.push_back(make_tuple(roundedString, entry.type)); } } @@ -789,17 +791,27 @@ void UnitConverter::InitializeSelectedUnits() vector curUnits = itr->second; if (!curUnits.empty()) { + // Units may already have been initialized through UnitConverter::RestoreUserPreferences(). + // Check if they have been, and if so, do not override restored units. + bool isFromUnitValid = m_fromType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_fromType) != curUnits.end(); + bool isToUnitValid = m_toType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_toType) != curUnits.end(); + + if (isFromUnitValid && isToUnitValid) + { + return; + } + bool conversionSourceSet = false; bool conversionTargetSet = false; for (const Unit& cur : curUnits) { - if (!conversionSourceSet && cur.isConversionSource) + if (!conversionSourceSet && cur.isConversionSource && !isFromUnitValid) { m_fromType = cur; conversionSourceSet = true; } - if (!conversionTargetSet && cur.isConversionTarget) + if (!conversionTargetSet && cur.isConversionTarget && !isToUnitValid) { m_toType = cur; conversionTargetSet = true; @@ -843,100 +855,61 @@ void UnitConverter::Calculate() { m_returnDisplay = m_currentDisplay; m_returnHasDecimal = m_currentHasDecimal; - TrimString(m_returnDisplay); + TrimTrailingZeros(m_returnDisplay); UpdateViewModel(); return; } unordered_map conversionTable = m_ratioMap[m_fromType]; - double returnValue = stod(m_currentDisplay); - if (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0) + if (AnyUnitIsEmpty() || (conversionTable[m_toType].ratio == 1.0 && conversionTable[m_toType].offset == 0.0)) { m_returnDisplay = m_currentDisplay; m_returnHasDecimal = m_currentHasDecimal; - TrimString(m_returnDisplay); + TrimTrailingZeros(m_returnDisplay); } else { - returnValue = Convert(returnValue, conversionTable[m_toType]); - m_returnDisplay = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED); - TrimString(m_returnDisplay); - int numPreDecimal = (int)m_returnDisplay.size(); - if (m_returnDisplay.find(L'.') != m_returnDisplay.npos) - { - numPreDecimal = (int)m_returnDisplay.find(L'.'); - } - if (returnValue < 0) - { - numPreDecimal--; - } + double currentValue = stod(m_currentDisplay); + double returnValue = Convert(currentValue, conversionTable[m_toType]); - if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) + auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory); + if (isCurrencyConverter) { - wstringstream out(wstringstream::out); - out << scientific << returnValue; - m_returnDisplay = out.str(); + // We don't need to trim the value when it's a currency. + m_returnDisplay = RoundSignificantDigits(returnValue, MAXIMUMDIGITSALLOWED); + TrimTrailingZeros(m_returnDisplay); } else { - returnValue = stod(m_returnDisplay); - wstring returnString; - if (m_currentDisplay.size() <= OPTIMALDIGITSALLOWED && abs(returnValue) >= OPTIMALDECIMALALLOWED) + int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue); + if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) { - returnString = RoundSignificant(returnValue, OPTIMALDIGITSALLOWED - min(numPreDecimal, OPTIMALDIGITSALLOWED)); + m_returnDisplay = ToScientificNumber(returnValue); } else { - returnString = RoundSignificant(returnValue, MAXIMUMDIGITSALLOWED - min(numPreDecimal, MAXIMUMDIGITSALLOWED)); + int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay); + int precision = 0; + if (abs(returnValue) < OPTIMALDECIMALALLOWED) + { + precision = MAXIMUMDIGITSALLOWED; + } + else + { + // Fewer digits are needed following the decimal if the number is large, + // we calculate the number of decimals necessary based on the number of digits in the integer part. + precision = max(0, max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits)) - numPreDecimal); + } + + m_returnDisplay = RoundSignificantDigits(returnValue, precision); + TrimTrailingZeros(m_returnDisplay); } - m_returnDisplay = returnString; - TrimString(m_returnDisplay); + m_returnHasDecimal = (m_returnDisplay.find(L'.') != wstring::npos); } - m_returnHasDecimal = (m_returnDisplay.find(L'.') != m_returnDisplay.npos); } UpdateViewModel(); } -/// -/// Trims out any trailing zeros or decimals in the given input string -/// -/// wstring to trim -void UnitConverter::TrimString(wstring& returnString) -{ - if (returnString.find(L'.') == m_returnDisplay.npos) - { - return; - } - - wstring::iterator iter; - for (iter = returnString.end() - 1;; iter--) - { - if (*iter != L'0') - { - returnString.erase(iter + 1, returnString.end()); - break; - } - } - if (*(returnString.end() - 1) == L'.') - { - returnString.erase(returnString.end() - 1, returnString.end()); - } -} - -/// -/// Rounds the given double to the given number of significant digits -/// -/// input double -/// int number of significant digits to round to -wstring UnitConverter::RoundSignificant(double num, int numSignificant) -{ - wstringstream out(wstringstream::out); - out << fixed; - out.precision(numSignificant); - out << num; - return out.str(); -} - void UnitConverter::UpdateCurrencySymbols() { if (m_currencyDataLoader != nullptr && m_vmCurrencyCallback != nullptr) diff --git a/src/CalcManager/UnitConverter.h b/src/CalcManager/UnitConverter.h index 0d1b8acd8..27b8629d9 100644 --- a/src/CalcManager/UnitConverter.h +++ b/src/CalcManager/UnitConverter.h @@ -279,9 +279,7 @@ namespace UnitConversionManager double Convert(double value, ConversionData conversionData); std::vector> CalculateSuggested(); void ClearValues(); - void TrimString(std::wstring& input); void InitializeSelectedUnits(); - std::wstring RoundSignificant(double num, int numSignificant); Category StringToCategory(const std::wstring& w); std::wstring CategoryToString(const Category& c, const wchar_t* delimiter); std::wstring UnitToString(const Unit& u, const wchar_t* delimiter); diff --git a/src/CalcManager/pch.h b/src/CalcManager/pch.h index 2bcedb7ac..9207ebff8 100644 --- a/src/CalcManager/pch.h +++ b/src/CalcManager/pch.h @@ -20,3 +20,5 @@ #include #include #include +#include +#include diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index b23edf7d8..90e55e695 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/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" @@ -31,6 +31,8 @@ using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; +using namespace Windows::Foundation; +using namespace Concurrency; namespace { @@ -38,14 +40,14 @@ namespace StringReference ClearMemoryVisibilityPropertyName(L"ClearMemoryVisibility"); } -ApplicationViewModel::ApplicationViewModel() : - m_CalculatorViewModel(nullptr), - m_DateCalcViewModel(nullptr), - m_GraphingCalcViewModel(nullptr), - m_ConverterViewModel(nullptr), - m_PreviousMode(ViewMode::None), - m_mode(ViewMode::None), - m_categories(nullptr) +ApplicationViewModel::ApplicationViewModel() + : m_CalculatorViewModel(nullptr) + , m_DateCalcViewModel(nullptr) + , m_GraphingCalcViewModel(nullptr) + , m_ConverterViewModel(nullptr) + , m_PreviousMode(ViewMode::None) + , m_mode(ViewMode::None) + , m_categories(nullptr) { SetMenuCategories(); } @@ -56,6 +58,7 @@ void ApplicationViewModel::Mode::set(ViewMode value) { PreviousMode = m_mode; m_mode = value; + SetDisplayNormalAlwaysOnTopOption(); OnModeChanged(); RaisePropertyChanged(ModePropertyName); } @@ -83,7 +86,7 @@ void ApplicationViewModel::Initialize(ViewMode mode) } catch (const std::exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance().LogStandardException(mode, __FUNCTIONW__, e); if (!TryRecoverFromNavigationModeFailure()) { // Could not navigate to standard mode either. @@ -93,7 +96,7 @@ void ApplicationViewModel::Initialize(ViewMode mode) } catch (Exception ^ e) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, e); + TraceLogger::GetInstance().LogPlatformException(mode, __FUNCTIONW__, e); if (!TryRecoverFromNavigationModeFailure()) { // Could not navigate to standard mode either. @@ -122,10 +125,8 @@ bool ApplicationViewModel::TryRecoverFromNavigationModeFailure() void ApplicationViewModel::OnModeChanged() { assert(NavCategory::IsValidViewMode(m_mode)); - TraceLogger::GetInstance().LogModeChangeBegin(m_PreviousMode, m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (NavCategory::IsCalculatorViewMode(m_mode)) { - TraceLogger::GetInstance().LogCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_CalculatorViewModel) { m_CalculatorViewModel = ref new StandardCalculatorViewModel(); @@ -141,7 +142,6 @@ void ApplicationViewModel::OnModeChanged() } else if (NavCategory::IsDateCalculatorViewMode(m_mode)) { - TraceLogger::GetInstance().LogDateCalculatorModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_DateCalcViewModel) { m_DateCalcViewModel = ref new DateCalculatorViewModel(); @@ -149,7 +149,6 @@ void ApplicationViewModel::OnModeChanged() } else if (NavCategory::IsConverterViewMode(m_mode)) { - TraceLogger::GetInstance().LogConverterModeViewed(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); if (!m_ConverterViewModel) { auto dataLoader = make_shared(ref new GeographicRegion()); @@ -163,13 +162,21 @@ void ApplicationViewModel::OnModeChanged() auto resProvider = AppResourceProvider::GetInstance(); CategoryName = resProvider.GetResourceString(NavCategory::GetNameResourceKey(m_mode)); - // This is the only place where a ViewMode enum should be cast to an int. - // + // Cast mode to an int in order to save it to app data. // Save the changed mode, so that the new window launches in this mode. // Don't save until after we have adjusted to the new mode, so we don't save a mode that fails to load. ApplicationData::Current->LocalSettings->Values->Insert(ModePropertyName, NavCategory::Serialize(m_mode)); - TraceLogger::GetInstance().LogModeChangeEnd(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + // Log ModeChange event when not first launch, log WindowCreated on first launch + if (NavCategory::IsValidViewMode(m_PreviousMode)) + { + TraceLogger::GetInstance().LogModeChange(m_mode); + } + else + { + TraceLogger::GetInstance().LogWindowCreated(m_mode, ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + } + RaisePropertyChanged(ClearMemoryVisibilityPropertyName); } @@ -208,3 +215,62 @@ void ApplicationViewModel::SetMenuCategories() // property setter logic. Categories = NavCategoryGroup::CreateMenuOptions(); } + +void ApplicationViewModel::ToggleAlwaysOnTop(float width, float height) +{ + HandleToggleAlwaysOnTop(width, height); +} + +#pragma optimize("", off) +task ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float height) +{ + if (ApplicationView::GetForCurrentView()->ViewMode == ApplicationViewMode::CompactOverlay) + { + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; + localSettings->Values->Insert(WidthLocalSettings, width); + localSettings->Values->Insert(HeightLocalSettings, height); + + bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::Default); + CalculatorViewModel->AreHistoryShortcutsEnabled = success; + CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = success; + CalculatorViewModel->IsAlwaysOnTop = !success; + IsAlwaysOnTop = !success; + } + else + { + ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; + ViewModePreferences ^ compactOptions = ViewModePreferences::CreateDefault(ApplicationViewMode::CompactOverlay); + if (!localSettings->Values->GetView()->HasKey(LaunchedLocalSettings)) + { + compactOptions->CustomSize = Size(320, 394); + localSettings->Values->Insert(LaunchedLocalSettings, true); + } + else + { + if (localSettings->Values->GetView()->HasKey(WidthLocalSettings) && localSettings->Values->GetView()->HasKey(HeightLocalSettings)) + { + float oldWidth = safe_cast(localSettings->Values->GetView()->Lookup(WidthLocalSettings))->GetSingle(); + float oldHeight = safe_cast(localSettings->Values->GetView()->Lookup(HeightLocalSettings))->GetSingle(); + compactOptions->CustomSize = Size(oldWidth, oldHeight); + } + else + { + compactOptions->CustomSize = Size(320, 394); + } + } + + bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::CompactOverlay, compactOptions); + CalculatorViewModel->AreHistoryShortcutsEnabled = !success; + CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = !success; + CalculatorViewModel->IsAlwaysOnTop = success; + IsAlwaysOnTop = success; + } + SetDisplayNormalAlwaysOnTopOption(); +}; +#pragma optimize("", on) + +void ApplicationViewModel::SetDisplayNormalAlwaysOnTopOption() +{ + DisplayNormalAlwaysOnTopOption = + m_mode == ViewMode::Standard && ApplicationView::GetForCurrentView()->IsViewModeSupported(ApplicationViewMode::CompactOverlay) && !IsAlwaysOnTop; +} diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index 7f85e5b2f..7e4238d8a 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -20,13 +20,17 @@ namespace CalculatorApp void Initialize(CalculatorApp::Common::ViewMode mode); // Use for first init, use deserialize for rehydration 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(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); OBSERVABLE_NAMED_PROPERTY_RW(Platform::String ^, CategoryName); + // Indicates whether calculator is currently in standard mode _and_ supports CompactOverlay _and_ is not in Always-on-Top mode + OBSERVABLE_PROPERTY_R(bool, DisplayNormalAlwaysOnTopOption); + COMMAND_FOR_METHOD(CopyCommand, ApplicationViewModel::OnCopyCommand); COMMAND_FOR_METHOD(PasteCommand, ApplicationViewModel::OnPasteCommand); @@ -66,6 +70,32 @@ namespace CalculatorApp } } + static property Platform::String ^ LaunchedLocalSettings + { + Platform::String ^ get() + { + return Platform::StringReference(L"calculatorAlwaysOnTopLaunched"); + } + } + + static property Platform::String ^ WidthLocalSettings + { + Platform::String ^ get() + { + return Platform::StringReference(L"calculatorAlwaysOnTopLastWidth"); + } + } + + static property Platform::String ^ HeightLocalSettings + { + Platform::String ^ get() + { + return Platform::StringReference(L"calculatorAlwaysOnTopLastHeight"); + } + } + + void ToggleAlwaysOnTop(float width, float height); + private: bool TryRecoverFromNavigationModeFailure(); @@ -78,6 +108,8 @@ namespace CalculatorApp CalculatorApp::Common::ViewMode m_mode; Windows::Foundation::Collections::IObservableVector ^ m_categories; + Concurrency::task HandleToggleAlwaysOnTop(float width, float height); + void SetDisplayNormalAlwaysOnTopOption(); }; } } diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index 545c65427..f5085a261 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -42,8 +42,8 @@ 14.0 true Windows Store - 10.0 - 10.0.18362.0 + 10.0.18362.0 + 10.0.17134.0 10.0 @@ -308,7 +308,7 @@ - /DSEND_TELEMETRY %(AdditionalOptions) + /DSEND_DIAGNOSTICS %(AdditionalOptions) @@ -322,6 +322,7 @@ + diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index 0afa090f3..4810f72fd 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -1,95 +1,247 @@  - + + + {1daab7c4-63f6-4266-a259-f34acad66d09} + + + {8d4edf06-c312-4312-978a-b6c2beb8295a} + + + {0184f727-b8aa-4af8-a699-63f1b56e7853} + + + {cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c} + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + DataLoaders + + + DataLoaders + + + DataLoaders + + + GraphingCalculator + + + GraphingCalculator + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + Common\Automation + + + DataLoaders + + + DataLoaders + + + DataLoaders + + + DataLoaders + + + DataLoaders + + + Common + + + DataLoaders + + + DataLoaders + + + Common + + + GraphingCalculator + + + GraphingCalculator + - + + DataLoaders + \ No newline at end of file diff --git a/src/CalcViewModel/Common/BitLength.h b/src/CalcViewModel/Common/BitLength.h new file mode 100644 index 000000000..8c38e7e40 --- /dev/null +++ b/src/CalcViewModel/Common/BitLength.h @@ -0,0 +1,17 @@ +#pragma once + +namespace CalculatorApp +{ + namespace Common + { + public + enum class BitLength : int + { + BitLengthUnknown = -1, + BitLengthByte = 8, + BitLengthWord = 16, + BitLengthDWord = 32, + BitLengthQWord = 64, + }; + } +} diff --git a/src/CalcViewModel/Common/CalculatorButtonUser.h b/src/CalcViewModel/Common/CalculatorButtonUser.h index b4f1bd0f8..5bf2cbe2d 100644 --- a/src/CalcViewModel/Common/CalculatorButtonUser.h +++ b/src/CalcViewModel/Common/CalculatorButtonUser.h @@ -12,171 +12,35 @@ namespace CalculatorApp public enum class NumbersAndOperatorsEnum { - Zero = (int) CM::Command::Command0, - One = (int) CM::Command::Command1, - Two = (int) CM::Command::Command2, - Three = (int) CM::Command::Command3, - Four = (int) CM::Command::Command4, - Five = (int) CM::Command::Command5, - Six = (int) CM::Command::Command6, - Seven = (int) CM::Command::Command7, - Eight = (int) CM::Command::Command8, - Nine = (int) CM::Command::Command9, - Add = (int) CM::Command::CommandADD, - Subtract = (int) CM::Command::CommandSUB, - Multiply = (int) CM::Command::CommandMUL, - Divide = (int) CM::Command::CommandDIV, - Invert = (int) CM::Command::CommandREC, - Equals = (int) CM::Command::CommandEQU, - Decimal = (int) CM::Command::CommandPNT, - Sqrt = (int) CM::Command::CommandSQRT, - Percent = (int) CM::Command::CommandPERCENT, - Negate = (int) CM::Command::CommandSIGN, - Backspace = (int) CM::Command::CommandBACK, - ClearEntry = (int) CM::Command::CommandCENTR, - Clear = (int) CM::Command::CommandCLEAR, - Degree = (int) CM::Command::CommandDEG, - Radians = (int) CM::Command::CommandRAD, - Grads = (int) CM::Command::CommandGRAD, - Degrees = (int) CM::Command::CommandDegrees, - OpenParenthesis = (int) CM::Command::CommandOPENP, - CloseParenthesis = (int) CM::Command::CommandCLOSEP, - Pi = (int) CM::Command::CommandPI, - Sin = (int) CM::Command::CommandSIN, - Cos = (int) CM::Command::CommandCOS, - Tan = (int) CM::Command::CommandTAN, - Factorial = (int) CM::Command::CommandFAC, - XPower2 = (int) CM::Command::CommandSQR, - Mod = (int) CM::Command::CommandMOD, - FToE = (int) CM::Command::CommandFE, - LogBaseE = (int) CM::Command::CommandLN, - InvSin = (int) CM::Command::CommandASIN, - InvCos = (int) CM::Command::CommandACOS, - InvTan = (int) CM::Command::CommandATAN, - LogBase10 = (int) CM::Command::CommandLOG, - XPowerY = (int) CM::Command::CommandPWR, - YRootX = (int) CM::Command::CommandROOT, - TenPowerX = (int) CM::Command::CommandPOW10, - EPowerX = (int) CM::Command::CommandPOWE, - Exp = (int) CM::Command::CommandEXP, - IsScientificMode = (int) CM::Command::ModeScientific, - IsStandardMode = (int) CM::Command::ModeBasic, - None = (int) CM::Command::CommandNULL, - IsProgrammerMode = (int) CM::Command::ModeProgrammer, - And = (int) CM::Command::CommandAnd, - Ror = (int) CM::Command::CommandROR, - Rol = (int) CM::Command::CommandROL, - Or = (int) CM::Command::CommandOR, - Lsh = (int) CM::Command::CommandLSHF, - Rsh = (int) CM::Command::CommandRSHF, - Xor = (int) CM::Command::CommandXor, - Not = (int) CM::Command::CommandNot, - A = (int) CM::Command::CommandA, - B = (int) CM::Command::CommandB, - C = (int) CM::Command::CommandC, - D = (int) CM::Command::CommandD, - E = (int) CM::Command::CommandE, - F = (int) CM::Command::CommandF, - Memory, // This is the memory button. Doesn't have a direct mapping to the CalcEngine. - Sinh = (int) CM::Command::CommandSINH, - Cosh = (int) CM::Command::CommandCOSH, - Tanh = (int) CM::Command::CommandTANH, - InvSinh = (int) CM::Command::CommandASINH, - InvCosh = (int) CM::Command::CommandACOSH, - InvTanh = (int) CM::Command::CommandATANH, - 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, - BINPOS1 = (int) CM::Command::CommandBINPOS1, - BINPOS2 = (int) CM::Command::CommandBINPOS2, - BINPOS3 = (int) CM::Command::CommandBINPOS3, - BINPOS4 = (int) CM::Command::CommandBINPOS4, - BINPOS5 = (int) CM::Command::CommandBINPOS5, - BINPOS6 = (int) CM::Command::CommandBINPOS6, - BINPOS7 = (int) CM::Command::CommandBINPOS7, - BINPOS8 = (int) CM::Command::CommandBINPOS8, - BINPOS9 = (int) CM::Command::CommandBINPOS9, - BINPOS10 = (int) CM::Command::CommandBINPOS10, - BINPOS11 = (int) CM::Command::CommandBINPOS11, - BINPOS12 = (int) CM::Command::CommandBINPOS12, - BINPOS13 = (int) CM::Command::CommandBINPOS13, - BINPOS14 = (int) CM::Command::CommandBINPOS14, - BINPOS15 = (int) CM::Command::CommandBINPOS15, - BINPOS16 = (int) CM::Command::CommandBINPOS16, - BINPOS17 = (int) CM::Command::CommandBINPOS17, - BINPOS18 = (int) CM::Command::CommandBINPOS18, - BINPOS19 = (int) CM::Command::CommandBINPOS19, - BINPOS20 = (int) CM::Command::CommandBINPOS20, - BINPOS21 = (int) CM::Command::CommandBINPOS21, - BINPOS22 = (int) CM::Command::CommandBINPOS22, - BINPOS23 = (int) CM::Command::CommandBINPOS23, - BINPOS24 = (int) CM::Command::CommandBINPOS24, - BINPOS25 = (int) CM::Command::CommandBINPOS25, - BINPOS26 = (int) CM::Command::CommandBINPOS26, - BINPOS27 = (int) CM::Command::CommandBINPOS27, - BINPOS28 = (int) CM::Command::CommandBINPOS28, - BINPOS29 = (int) CM::Command::CommandBINPOS29, - BINPOS30 = (int) CM::Command::CommandBINPOS30, - BINPOS31 = (int) CM::Command::CommandBINPOS31, - BINPOS32 = (int) CM::Command::CommandBINPOS32, - BINPOS33 = (int) CM::Command::CommandBINPOS33, - BINPOS34 = (int) CM::Command::CommandBINPOS34, - BINPOS35 = (int) CM::Command::CommandBINPOS35, - BINPOS36 = (int) CM::Command::CommandBINPOS36, - BINPOS37 = (int) CM::Command::CommandBINPOS37, - BINPOS38 = (int) CM::Command::CommandBINPOS38, - BINPOS39 = (int) CM::Command::CommandBINPOS39, - BINPOS40 = (int) CM::Command::CommandBINPOS40, - BINPOS41 = (int) CM::Command::CommandBINPOS41, - BINPOS42 = (int) CM::Command::CommandBINPOS42, - BINPOS43 = (int) CM::Command::CommandBINPOS43, - BINPOS44 = (int) CM::Command::CommandBINPOS44, - BINPOS45 = (int) CM::Command::CommandBINPOS45, - BINPOS46 = (int) CM::Command::CommandBINPOS46, - BINPOS47 = (int) CM::Command::CommandBINPOS47, - BINPOS48 = (int) CM::Command::CommandBINPOS48, - BINPOS49 = (int) CM::Command::CommandBINPOS49, - BINPOS50 = (int) CM::Command::CommandBINPOS50, - BINPOS51 = (int) CM::Command::CommandBINPOS51, - BINPOS52 = (int) CM::Command::CommandBINPOS52, - BINPOS53 = (int) CM::Command::CommandBINPOS53, - BINPOS54 = (int) CM::Command::CommandBINPOS54, - BINPOS55 = (int) CM::Command::CommandBINPOS55, - BINPOS56 = (int) CM::Command::CommandBINPOS56, - BINPOS57 = (int) CM::Command::CommandBINPOS57, - BINPOS58 = (int) CM::Command::CommandBINPOS58, - BINPOS59 = (int) CM::Command::CommandBINPOS59, - BINPOS60 = (int) CM::Command::CommandBINPOS60, - BINPOS61 = (int) CM::Command::CommandBINPOS61, - BINPOS62 = (int) CM::Command::CommandBINPOS62, - BINPOS63 = (int) CM::Command::CommandBINPOS63, - BINEND = (int) CM::Command::CommandBINEDITEND - }; - - // This contains list of functions whose usage we are tracelogging -public - enum class FunctionLogEnum - { + Zero = (int)CM::Command::Command0, + One = (int)CM::Command::Command1, + Two = (int)CM::Command::Command2, + Three = (int)CM::Command::Command3, + Four = (int)CM::Command::Command4, + Five = (int)CM::Command::Command5, + Six = (int)CM::Command::Command6, + Seven = (int)CM::Command::Command7, + Eight = (int)CM::Command::Command8, + Nine = (int)CM::Command::Command9, + Add = (int)CM::Command::CommandADD, + Subtract = (int)CM::Command::CommandSUB, + Multiply = (int)CM::Command::CommandMUL, + Divide = (int)CM::Command::CommandDIV, Invert = (int)CM::Command::CommandREC, + Equals = (int)CM::Command::CommandEQU, + Decimal = (int)CM::Command::CommandPNT, Sqrt = (int)CM::Command::CommandSQRT, Percent = (int)CM::Command::CommandPERCENT, Negate = (int)CM::Command::CommandSIGN, + Backspace = (int)CM::Command::CommandBACK, + ClearEntry = (int)CM::Command::CommandCENTR, + Clear = (int)CM::Command::CommandCLEAR, + Degree = (int)CM::Command::CommandDEG, + Radians = (int)CM::Command::CommandRAD, + Grads = (int)CM::Command::CommandGRAD, Degrees = (int)CM::Command::CommandDegrees, + OpenParenthesis = (int)CM::Command::CommandOPENP, + CloseParenthesis = (int)CM::Command::CommandCLOSEP, Pi = (int)CM::Command::CommandPI, Sin = (int)CM::Command::CommandSIN, Cos = (int)CM::Command::CommandCOS, @@ -195,10 +59,10 @@ public TenPowerX = (int)CM::Command::CommandPOW10, EPowerX = (int)CM::Command::CommandPOWE, Exp = (int)CM::Command::CommandEXP, - DecButton = (int)CM::Command::CommandDec, - OctButton = (int)CM::Command::CommandOct, - HexButton = (int)CM::Command::CommandHex, - BinButton = (int)CM::Command::CommandBin, + IsScientificMode = (int)CM::Command::ModeScientific, + IsStandardMode = (int)CM::Command::ModeBasic, + None = (int)CM::Command::CommandNULL, + IsProgrammerMode = (int)CM::Command::ModeProgrammer, And = (int)CM::Command::CommandAnd, Ror = (int)CM::Command::CommandROR, Rol = (int)CM::Command::CommandROL, @@ -207,13 +71,108 @@ public Rsh = (int)CM::Command::CommandRSHF, Xor = (int)CM::Command::CommandXor, Not = (int)CM::Command::CommandNot, + A = (int)CM::Command::CommandA, + B = (int)CM::Command::CommandB, + C = (int)CM::Command::CommandC, + D = (int)CM::Command::CommandD, + E = (int)CM::Command::CommandE, + F = (int)CM::Command::CommandF, + Memory, // This is the memory button. Doesn't have a direct mapping to the CalcEngine. Sinh = (int)CM::Command::CommandSINH, Cosh = (int)CM::Command::CommandCOSH, Tanh = (int)CM::Command::CommandTANH, InvSinh = (int)CM::Command::CommandASINH, InvCosh = (int)CM::Command::CommandACOSH, InvTanh = (int)CM::Command::CommandATANH, - Cube = (int)CM::Command::CommandCUB, - DMS = (int)CM::Command::CommandDMS, + 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, + BINPOS1 = (int)CM::Command::CommandBINPOS1, + BINPOS2 = (int)CM::Command::CommandBINPOS2, + BINPOS3 = (int)CM::Command::CommandBINPOS3, + BINPOS4 = (int)CM::Command::CommandBINPOS4, + BINPOS5 = (int)CM::Command::CommandBINPOS5, + BINPOS6 = (int)CM::Command::CommandBINPOS6, + BINPOS7 = (int)CM::Command::CommandBINPOS7, + BINPOS8 = (int)CM::Command::CommandBINPOS8, + BINPOS9 = (int)CM::Command::CommandBINPOS9, + BINPOS10 = (int)CM::Command::CommandBINPOS10, + BINPOS11 = (int)CM::Command::CommandBINPOS11, + BINPOS12 = (int)CM::Command::CommandBINPOS12, + BINPOS13 = (int)CM::Command::CommandBINPOS13, + BINPOS14 = (int)CM::Command::CommandBINPOS14, + BINPOS15 = (int)CM::Command::CommandBINPOS15, + BINPOS16 = (int)CM::Command::CommandBINPOS16, + BINPOS17 = (int)CM::Command::CommandBINPOS17, + BINPOS18 = (int)CM::Command::CommandBINPOS18, + BINPOS19 = (int)CM::Command::CommandBINPOS19, + BINPOS20 = (int)CM::Command::CommandBINPOS20, + BINPOS21 = (int)CM::Command::CommandBINPOS21, + BINPOS22 = (int)CM::Command::CommandBINPOS22, + BINPOS23 = (int)CM::Command::CommandBINPOS23, + BINPOS24 = (int)CM::Command::CommandBINPOS24, + BINPOS25 = (int)CM::Command::CommandBINPOS25, + BINPOS26 = (int)CM::Command::CommandBINPOS26, + BINPOS27 = (int)CM::Command::CommandBINPOS27, + BINPOS28 = (int)CM::Command::CommandBINPOS28, + BINPOS29 = (int)CM::Command::CommandBINPOS29, + BINPOS30 = (int)CM::Command::CommandBINPOS30, + BINPOS31 = (int)CM::Command::CommandBINPOS31, + BINPOS32 = (int)CM::Command::CommandBINPOS32, + BINPOS33 = (int)CM::Command::CommandBINPOS33, + BINPOS34 = (int)CM::Command::CommandBINPOS34, + BINPOS35 = (int)CM::Command::CommandBINPOS35, + BINPOS36 = (int)CM::Command::CommandBINPOS36, + BINPOS37 = (int)CM::Command::CommandBINPOS37, + BINPOS38 = (int)CM::Command::CommandBINPOS38, + BINPOS39 = (int)CM::Command::CommandBINPOS39, + BINPOS40 = (int)CM::Command::CommandBINPOS40, + BINPOS41 = (int)CM::Command::CommandBINPOS41, + BINPOS42 = (int)CM::Command::CommandBINPOS42, + BINPOS43 = (int)CM::Command::CommandBINPOS43, + BINPOS44 = (int)CM::Command::CommandBINPOS44, + BINPOS45 = (int)CM::Command::CommandBINPOS45, + BINPOS46 = (int)CM::Command::CommandBINPOS46, + BINPOS47 = (int)CM::Command::CommandBINPOS47, + BINPOS48 = (int)CM::Command::CommandBINPOS48, + BINPOS49 = (int)CM::Command::CommandBINPOS49, + BINPOS50 = (int)CM::Command::CommandBINPOS50, + BINPOS51 = (int)CM::Command::CommandBINPOS51, + BINPOS52 = (int)CM::Command::CommandBINPOS52, + BINPOS53 = (int)CM::Command::CommandBINPOS53, + BINPOS54 = (int)CM::Command::CommandBINPOS54, + BINPOS55 = (int)CM::Command::CommandBINPOS55, + BINPOS56 = (int)CM::Command::CommandBINPOS56, + BINPOS57 = (int)CM::Command::CommandBINPOS57, + BINPOS58 = (int)CM::Command::CommandBINPOS58, + BINPOS59 = (int)CM::Command::CommandBINPOS59, + BINPOS60 = (int)CM::Command::CommandBINPOS60, + BINPOS61 = (int)CM::Command::CommandBINPOS61, + BINPOS62 = (int)CM::Command::CommandBINPOS62, + BINPOS63 = (int)CM::Command::CommandBINPOS63, + BINEND = (int)CM::Command::CommandBINEDITEND, + + // Enum values below are used for Tracelogging and do not map to the Calculator engine + MemoryAdd = (int)CM::Command::CommandMPLUS, + MemorySubtract = (int)CM::Command::CommandMMINUS, + MemoryRecall = (int)CM::Command::CommandRECALL, + MemoryClear = (int)CM::Command::CommandMCLEAR, + BitflipButton = 1000, + FullKeypadButton = 1001 }; } diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index db2439f72..89c401dcd 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -65,7 +65,7 @@ void CopyPasteManager::CopyToClipboard(String ^ stringToCopy) Clipboard::SetContent(dataPackage); } -task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +task CopyPasteManager::GetStringToPaste(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, BitLength bitLengthType) { // Retrieve the text in the clipboard auto dataPackageView = Clipboard::GetContent(); @@ -97,19 +97,19 @@ int CopyPasteManager::ClipboardTextFormat() return -1; } -String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, int programmerNumberBase, int bitLengthType) +String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, int programmerNumberBase, BitLength bitLengthType) { return CopyPasteManager::ValidatePasteExpression(pastedText, mode, NavCategory::GetGroupType(mode), programmerNumberBase, bitLengthType); } // return "NoOp" if pastedText is invalid else return pastedText -String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, BitLength bitLengthType) { if (pastedText->Length() > MaxPasteableLength) { // return NoOp to indicate don't paste anything. - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"PastedExpressionSizeGreaterThanMaxAllowed", mode, programmerNumberBase, bitLengthType); + TraceLogger::GetInstance().LogError(mode, L"CopyPasteManager::ValidatePasteExpression", L"PastedExpressionSizeGreaterThanMaxAllowed"); return StringReference(PasteErrorString); } @@ -129,7 +129,7 @@ String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode // Extract operands from the expression to make regex comparison easy and quick. For whole expression it was taking too much of time. // Operands vector will have the list of operands in the pasteExpression - vector operands = ExtractOperands(pasteExpression, mode, programmerNumberBase, bitLengthType); + vector operands = ExtractOperands(pasteExpression, mode); if (operands.empty()) { // return NoOp to indicate don't paste anything. @@ -144,14 +144,14 @@ String ^ CopyPasteManager::ValidatePasteExpression(String ^ pastedText, ViewMode // validate each operand with patterns for different modes if (!ExpressionRegExMatch(operands, mode, modeType, programmerNumberBase, bitLengthType)) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"InvalidExpressionForPresentMode", mode, programmerNumberBase, bitLengthType); + TraceLogger::GetInstance().LogError(mode, L"CopyPasteManager::ValidatePasteExpression", L"InvalidExpressionForPresentMode"); return StringReference(PasteErrorString); } return ref new String(pastedText->Data()); } -vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode, int programmerNumberBase, int bitLengthType) +vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression, ViewMode mode) { vector operands{}; size_t lastIndex = 0; @@ -173,7 +173,7 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression if (operands.size() >= MaxOperandCount) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"OperandCountGreaterThanMaxCount", mode, programmerNumberBase, bitLengthType); + TraceLogger::GetInstance().LogError(mode, L"CopyPasteManager::ExtractOperands", L"OperandCountGreaterThanMaxCount"); operands.clear(); return operands; } @@ -187,7 +187,7 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression // to disallow pasting of 1e+12345 as 1e+1234, max exponent that can be pasted is 9999. if (expLength > MaxExponentLength) { - TraceLogger::GetInstance().LogInvalidPastedInputOccurred(L"ExponentLengthGreaterThanMaxLength", mode, programmerNumberBase, bitLengthType); + TraceLogger::GetInstance().LogError(mode, L"CopyPasteManager::ExtractOperands", L"ExponentLengthGreaterThanMaxLength"); operands.clear(); return operands; } @@ -241,7 +241,7 @@ vector CopyPasteManager::ExtractOperands(const wstring& pasteExpression return operands; } -bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, BitLength bitLengthType) { if (operands.empty()) { @@ -317,7 +317,7 @@ bool CopyPasteManager::ExpressionRegExMatch(vector operands, ViewMode m return expMatched; } -pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, int bitLengthType) +pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mode, CategoryGroupType modeType, int programmerNumberBase, BitLength bitLengthType) { constexpr size_t defaultMaxOperandLength = 0; constexpr uint64_t defaultMaxValue = 0; @@ -335,16 +335,16 @@ pair CopyPasteManager::GetMaxOperandLengthAndValue(ViewMode mo unsigned int bitLength = 0; switch (bitLengthType) { - case QwordType: + case BitLength::BitLengthQWord: bitLength = 64; break; - case DwordType: + case BitLength::BitLengthDWord: bitLength = 32; break; - case WordType: + case BitLength::BitLengthWord: bitLength = 16; break; - case ByteType: + case BitLength::BitLengthByte: bitLength = 8; break; } diff --git a/src/CalcViewModel/Common/CopyPasteManager.h b/src/CalcViewModel/Common/CopyPasteManager.h index b393dab52..9571cdbbd 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.h +++ b/src/CalcViewModel/Common/CopyPasteManager.h @@ -5,6 +5,7 @@ #include "AppResourceProvider.h" #include "NavCategory.h" +#include "BitLength.h" namespace CalculatorUnitTests { @@ -13,10 +14,6 @@ namespace CalculatorUnitTests namespace CalculatorApp { - inline constexpr auto QwordType = 1; - inline constexpr auto DwordType = 2; - inline constexpr auto WordType = 3; - inline constexpr auto ByteType = 4; inline constexpr auto HexBase = 5; inline constexpr auto DecBase = 6; inline constexpr auto OctBase = 7; @@ -30,7 +27,7 @@ namespace CalculatorApp CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, - int bitLengthType = -1); + CalculatorApp::Common::BitLength bitLengthType = CalculatorApp::Common::BitLength::BitLengthUnknown); static bool HasStringToPaste() { return ClipboardTextFormat() >= 0; @@ -41,29 +38,33 @@ namespace CalculatorApp private: static int ClipboardTextFormat(); static Platform::String - ^ ValidatePasteExpression(Platform::String ^ pastedText, CalculatorApp::Common::ViewMode mode, int programmerNumberBase, int bitLengthType); + ^ ValidatePasteExpression( + Platform::String ^ pastedText, + CalculatorApp::Common::ViewMode mode, + int programmerNumberBase, + CalculatorApp::Common::BitLength bitLengthType); static Platform::String ^ ValidatePasteExpression( Platform::String ^ pastedText, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase, - int bitLengthType); + CalculatorApp::Common::BitLength bitLengthType); static std::vector - ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode, int programmerNumberBase = -1, int bitLengthType = -1); + ExtractOperands(const std::wstring& pasteExpression, CalculatorApp::Common::ViewMode mode); static bool ExpressionRegExMatch( std::vector operands, CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, - int bitLengthType = -1); + CalculatorApp::Common::BitLength bitLengthType = CalculatorApp::Common::BitLength::BitLengthUnknown); static std::pair GetMaxOperandLengthAndValue( CalculatorApp::Common::ViewMode mode, CalculatorApp::Common::CategoryGroupType modeType, int programmerNumberBase = -1, - int bitLengthType = -1); + CalculatorApp::Common::BitLength bitLengthType = CalculatorApp::Common::BitLength::BitLengthUnknown); static std::wstring SanitizeOperand(const std::wstring& operand); static bool TryOperandToULL(const std::wstring& operand, int numberBase, unsigned long long int& result); static size_t OperandLength( diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp index 5f7510a30..993b231d0 100644 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp +++ b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp @@ -670,10 +670,11 @@ void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher ^, Accele if (nullptr != vm) { ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast(key)); - if (NavCategory::IsValidViewMode(toMode)) + auto nvi = dynamic_cast(menuItems->GetAt(NavCategory::GetFlatIndex(toMode))); + if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode)) { vm->Mode = toMode; - navView->SelectedItem = menuItems->GetAt(NavCategory::GetFlatIndex(toMode)); + navView->SelectedItem = nvi; } } } diff --git a/src/CalcViewModel/Common/LocalizationService.cpp b/src/CalcViewModel/Common/LocalizationService.cpp index 24354e4b3..eacc04de1 100644 --- a/src/CalcViewModel/Common/LocalizationService.cpp +++ b/src/CalcViewModel/Common/LocalizationService.cpp @@ -75,9 +75,24 @@ LocalizationService::LocalizationService(_In_ const wchar_t * const overridedLan { m_isLanguageOverrided = overridedLanguage != nullptr; m_language = m_isLanguageOverrided ? ref new Platform::String(overridedLanguage) : ApplicationLanguages::Languages->GetAt(0); - m_flowDirection = ResourceContext::GetForCurrentView()->QualifierValues->Lookup(L"LayoutDirection") + m_flowDirection = ResourceContext::GetForViewIndependentUse()->QualifierValues->Lookup(L"LayoutDirection") != L"LTR" ? FlowDirection::RightToLeft : FlowDirection::LeftToRight; + wstring localeName = wstring(m_language->Data()); + localeName += L".UTF8"; + try + { + // Convert wstring to string for locale + int size_needed = WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), NULL, 0, NULL, NULL); + string localeNameStr(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &localeName[0], (int)localeName.size(), &localeNameStr[0], size_needed, NULL, NULL); + + m_locale = locale(localeNameStr.data()); + } + catch (...) + { + m_locale = locale(""); + } auto resourceLoader = AppResourceProvider::GetInstance(); m_fontFamilyOverride = resourceLoader.GetResourceString(L"LocalizedFontFamilyOverride"); @@ -371,7 +386,7 @@ DecimalFormatter ^ LocalizationService::GetRegionalSettingsAwareDecimalFormatter // as configured by running intl.cpl. // // This helper function creates a DateTimeFormatter with a TwentyFour hour clock -DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String^ format) const +DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format) const { IIterable ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) @@ -384,7 +399,7 @@ DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatt // If successful, returns a formatter that respects the user's regional format settings, // as configured by running intl.cpl. -DateTimeFormatter^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const +DateTimeFormatter ^ LocalizationService::GetRegionalSettingsAwareDateTimeFormatter(_In_ String ^ format, _In_ String ^ calendarIdentifier, _In_ String ^ clockIdentifier) const { IIterable ^ languageIdentifiers = LocalizationService::GetLanguageIdentifiers(); if (languageIdentifiers == nullptr) @@ -421,7 +436,7 @@ IIterable ^ LocalizationService::GetLanguageIdentifiers() const if (m_isLanguageOverrided) { - auto overridedLanguageList = ref new Vector(); + auto overridedLanguageList = ref new Vector(); overridedLanguageList->Append(m_language); return overridedLanguageList; } @@ -562,3 +577,11 @@ String ^ LocalizationService::GetNarratorReadableString(String ^ rawString) return ref new String(readableString.str().c_str()); } + +void LocalizationService::Sort(std::vector& source) +{ + const collate& coll = use_facet>(m_locale); + sort(source.begin(), source.end(), [&coll](Platform::String ^ str1, Platform::String ^ str2) { + return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0; + }); +} diff --git a/src/CalcViewModel/Common/LocalizationService.h b/src/CalcViewModel/Common/LocalizationService.h index 736d5647a..6c5d0c518 100644 --- a/src/CalcViewModel/Common/LocalizationService.h +++ b/src/CalcViewModel/Common/LocalizationService.h @@ -30,9 +30,9 @@ namespace CalculatorApp DEPENDENCY_PROPERTY_ATTACHED_WITH_DEFAULT_AND_CALLBACK(LanguageFontType, FontType, LanguageFontType::UIText); DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(double, FontSize); - internal: - static LocalizationService^ GetInstance(); - static void OverrideWithLanguage(_In_ const wchar_t * const language); + internal: + static LocalizationService ^ GetInstance(); + static void OverrideWithLanguage(_In_ const wchar_t* const language); Windows::UI::Xaml::FlowDirection GetFlowDirection(); bool IsRtlLayout(); @@ -43,6 +43,19 @@ namespace CalculatorApp Windows::UI::Text::FontWeight GetFontWeightOverride(); double GetFontScaleFactorOverride(LanguageFontType fontType); + void Sort(std::vector& source); + + template + void Sort(std::vector& source, std::function func) + { + const collate& coll = use_facet>(m_locale); + sort(source.begin(), source.end(), [&coll, &func](T obj1, T obj2) { + Platform::String ^ str1 = func(obj1); + Platform::String ^ str2 = func(obj2); + return coll.compare(str1->Begin(), str1->End(), str2->Begin(), str2->End()) < 0; + }); + } + Windows::Globalization::NumberFormatting::DecimalFormatter ^ GetRegionalSettingsAwareDecimalFormatter() const; Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ GetRegionalSettingsAwareDateTimeFormatter(_In_ Platform::String ^ format) const; Windows::Globalization::DateTimeFormatting::DateTimeFormatter ^ GetRegionalSettingsAwareDateTimeFormatter( @@ -76,15 +89,16 @@ namespace CalculatorApp static LocalizationService ^ s_singletonInstance; - Windows::Globalization::Fonts::LanguageFontGroup ^ m_fontGroup; - Platform::String ^ m_language; - Windows::UI::Xaml::FlowDirection m_flowDirection; - bool m_overrideFontApiValues; - Platform::String ^ m_fontFamilyOverride; - bool m_isLanguageOverrided; - Windows::UI::Text::FontWeight m_fontWeightOverride; - double m_uiTextFontScaleFactorOverride; - double m_uiCaptionFontScaleFactorOverride; + Windows::Globalization::Fonts::LanguageFontGroup ^ m_fontGroup; + Platform::String ^ m_language; + Windows::UI::Xaml::FlowDirection m_flowDirection; + bool m_overrideFontApiValues; + Platform::String ^ m_fontFamilyOverride; + bool m_isLanguageOverrided; + Windows::UI::Text::FontWeight m_fontWeightOverride; + double m_uiTextFontScaleFactorOverride; + double m_uiCaptionFontScaleFactorOverride; + std::locale m_locale; }; } diff --git a/src/CalcViewModel/Common/NavCategory.cpp b/src/CalcViewModel/Common/NavCategory.cpp index ecccf1a9e..43e912ae1 100644 --- a/src/CalcViewModel/Common/NavCategory.cpp +++ b/src/CalcViewModel/Common/NavCategory.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" @@ -37,39 +37,163 @@ static constexpr int VOLUME_ID = 4; static constexpr int LENGTH_ID = 5; static constexpr int WEIGHT_ID = 6; static constexpr int TEMPERATURE_ID = 7; -static constexpr int ENERGY_ID = 8; -static constexpr int AREA_ID = 9; -static constexpr int SPEED_ID = 10; -static constexpr int TIME_ID = 11; -static constexpr int POWER_ID = 12; -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 ENERGY_ID = 8; +static constexpr int AREA_ID = 9; +static constexpr int SPEED_ID = 10; +static constexpr int TIME_ID = 11; +static constexpr int POWER_ID = 12; +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 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 }, - 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, POSITIVE_ONLY }, - 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, POSITIVE_ONLY } -}; +static constexpr array s_categoryManifest = { NavCategoryInitializer{ ViewMode::Standard, + STANDARD_ID, + L"Standard", + L"StandardMode", + L"\uE8EF", + CategoryGroupType::Calculator, + MyVirtualKey::Number1, + SUPPORTS_ALL }, + NavCategoryInitializer{ ViewMode::Graphing, + GRAPHING_ID, + L"Graphing", + L"GraphingCalculatorMode", + L"\uF770", + CategoryGroupType::Calculator, + MyVirtualKey::Number5, + 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, + POSITIVE_ONLY }, + 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, + POSITIVE_ONLY } }; // This function should only be used when storing the mode to app data. int NavCategory::Serialize(ViewMode mode) diff --git a/src/CalcViewModel/Common/TraceLogger.cpp b/src/CalcViewModel/Common/TraceLogger.cpp index f7d32d990..65375549f 100644 --- a/src/CalcViewModel/Common/TraceLogger.cpp +++ b/src/CalcViewModel/Common/TraceLogger.cpp @@ -19,94 +19,51 @@ using namespace winrt::Windows::System::UserProfile; namespace CalculatorApp { - static multimap> s_memoryMap; - static constexpr array s_programmerType{ L"N/A", L"QwordType", L"DwordType", L"WordType", L"ByteType", L"HexBase", L"DecBase", L"OctBase", L"BinBase" }; static reader_writer_lock s_traceLoggerLock; - // Telemetry events. Uploaded to asimov. - constexpr auto EVENT_NAME_DEBUG = L"Debug"; - constexpr auto EVENT_NAME_ERROR = L"ErrorMessage"; - constexpr auto EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM = L"AppPrelaunchedBySystem"; - constexpr auto EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER = L"PrelaunchedAppActivatedByUser"; - constexpr auto EVENT_NAME_APP_LAUNCH_BEGIN = L"AppLaunchBegin"; - constexpr auto EVENT_NAME_APP_LAUNCH_END = L"AppLaunchEnd"; - constexpr auto EVENT_NAME_APP_RESUME_END = L"AppResumeEnd"; - constexpr auto EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION = L"PreviousStateOnWindowCreation"; - constexpr auto EVENT_NAME_SIZE_ON_SUSPENSION = L"CalculatorSizeOnSuspension"; - constexpr auto EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION = L"CalculatorViewedInSession"; - constexpr auto EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION = L"DateCalculatorViewedInSession"; - constexpr auto EVENT_NAME_CONVERTER_VIEWED_IN_SESSION = L"ConverterViewedInSession"; - constexpr auto EVENT_NAME_MODE_CHANGE_BEGIN = L"ModeChangeBegin"; - constexpr auto EVENT_NAME_MODE_CHANGE_END = L"ModeChangeEnd"; - constexpr auto EVENT_NAME_HISTORY_BODY_OPENED = L"HistoryBodyOpened"; - constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN = L"HistoryItemLoadBegin"; - constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD_END = L"HistoryItemLoadEnd"; - constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN = L"HistoryFlyoutOpenBegin"; - constexpr auto EVENT_NAME_HISTORY_FLYOUT_OPEN_END = L"HistoryFlyoutOpenEnd"; - constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_BEGIN = L"NewWindowCreationBegin"; - constexpr auto EVENT_NAME_NEW_WINDOW_CREATION_END = L"NewWindowCreationEnd"; - constexpr auto EVENT_NAME_HISTORY_CLEAR = L"HistoryClearBegin"; - constexpr auto EVENT_NAME_MULTIPLE_MEMORY_USED = L"MultipleMemoryUsed"; - constexpr auto EVENT_NAME_SINGLE_MEMORY_USED = L"SingleMemoryUsed"; - constexpr auto EVENT_NAME_SHARED_MEMORY_USED = L"SharedMemoryUsed"; - constexpr auto EVENT_NAME_MEMORY_BODY_OPENED = L"MemoryBodyOpened"; - constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN = L"MemoryFlyoutOpenBegin"; - constexpr auto EVENT_NAME_MEMORY_FLYOUT_OPEN_END = L"MemoryFlyoutOpenEnd"; - constexpr auto EVENT_NAME_MEMORY_CLEAR_ALL = L"MemoryClearAll"; - constexpr auto EVENT_NAME_INVALID_PASTED_INPUT_OCCURRED = L"InvalidPastedInputOccurred"; - constexpr auto EVENT_NAME_VALID_INPUT_PASTED = L"ValidInputPasted"; - constexpr auto EVENT_NAME_BITFLIP_PANE_CLICKED = L"BitFlipPaneClicked"; - constexpr auto EVENT_NAME_BITFLIP_BUTTONS_USED = L"BitFlipToggleButtonUsed"; - constexpr auto EVENT_NAME_ANGLE_BUTTONS_USED = L"AngleButtonUsedInSession"; - constexpr auto EVENT_NAME_HYP_BUTTON_USED = L"HypButtonUsedInSession"; - constexpr auto EVENT_NAME_FUNCTION_USAGE = L"FunctionUsageInSession"; - constexpr auto EVENT_NAME_BITLENGTH_BUTTON_USED = L"BitLengthButtonUsed"; - constexpr auto EVENT_NAME_RADIX_BUTTON_USED = L"RadixButtonUsed"; - constexpr auto EVENT_NAME_MAX_WINDOW_COUNT = L"MaxWindowCountInSession"; - constexpr auto EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL = L"WindowActivatedThroughProtocol"; - constexpr auto EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH = L"WindowLaunchedThroughTile"; - constexpr auto EVENT_NAME_DATE_DIFFERENCE_USED = L"DateDifferenceModeUsed"; - constexpr auto EVENT_NAME_DATE_ADD_SUBTRACT_USED = L"DateAddSubtractModeUsed"; - constexpr auto EVENT_NAME_DATE_DIFFERENCE_FOUND = L"DateDifferenceFound"; - constexpr auto EVENT_NAME_HIDE_IF_SHOWN = L"HideIfShown"; - constexpr auto EVENT_NAME_ABOUT_FLYOUT_OPENED = L"AboutFlyoutOpened"; - constexpr auto EVENT_NAME_NAV_BAR_OPENED = L"NavBarOpened"; - constexpr auto EVENT_NAME_CORE_WINDOW_WAS_NULL = L"CoreWindowWasNull"; + // Diagnostics events. Uploaded to asimov. + constexpr auto EVENT_NAME_WINDOW_ON_CREATED = L"WindowCreated"; + constexpr auto EVENT_NAME_BUTTON_USAGE = L"ButtonUsageInSession"; + constexpr auto EVENT_NAME_NAV_BAR_OPENED = L"NavigationViewOpened"; + constexpr auto EVENT_NAME_MODE_CHANGED = L"ModeChanged"; + constexpr auto EVENT_NAME_DATE_CALCULATION_MODE_USED = L"DateCalculationModeUsed"; + constexpr auto EVENT_NAME_HISTORY_ITEM_LOAD = L"HistoryItemLoad"; + constexpr auto EVENT_NAME_MEMORY_ITEM_LOAD = L"MemoryItemLoad"; + constexpr auto EVENT_NAME_VISUAL_STATE_CHANGED = L"VisualStateChanged"; + constexpr auto EVENT_NAME_CONVERTER_INPUT_RECEIVED = L"ConverterInputReceived"; + constexpr auto EVENT_NAME_INPUT_PASTED = L"InputPasted"; constexpr auto EVENT_NAME_EXCEPTION = L"Exception"; constexpr auto PDT_PRIVACY_DATA_TAG = L"PartA_PrivTags"; constexpr auto PDT_PRODUCT_AND_SERVICE_USAGE = 0x0000'0000'0200'0000u; -#ifdef SEND_TELEMETRY +#ifdef SEND_DIAGNOSTICS // c.f. WINEVENT_KEYWORD_RESERVED_63-56 0xFF00000000000000 // Bits 63-56 - channel keywords // c.f. WINEVENT_KEYWORD_* 0x00FF000000000000 // Bits 55-48 - system-reserved keywords - constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0x0000800000000000; // Bit 47 - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0x0000400000000000; // Bit 46 - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0x0000200000000000; // Bit 45 - constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0x0000100000000000; // Bit 44 (reserved for future assignment) + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_1 = 0x0000800000000000; // Bit 47 + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_2 = 0x0000400000000000; // Bit 46 + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_3 = 0x0000200000000000; // Bit 45 #else - // define all Keyword options as 0 when we do not want to upload app telemetry - constexpr int64_t MICROSOFT_KEYWORD_CRITICAL_DATA = 0; - constexpr int64_t MICROSOFT_KEYWORD_MEASURES = 0; - constexpr int64_t MICROSOFT_KEYWORD_TELEMETRY = 0; - constexpr int64_t MICROSOFT_KEYWORD_RESERVED_44 = 0; + // define all Keyword options as 0 when we do not want to upload app diagnostics + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_1 = 0; + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_2 = 0; + constexpr int64_t MICROSOFT_KEYWORD_LEVEL_3 = 0; #endif #pragma region TraceLogger setup and cleanup TraceLogger::TraceLogger() : g_calculatorProvider( - L"MicrosoftCalculator", - LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), // Microsoft Telemetry group - GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 }) + L"MicrosoftCalculator", + LoggingChannelOptions(GUID{ 0x4f50731a, 0x89cf, 0x4782, 0xb3, 0xe0, 0xdc, 0xe8, 0xc9, 0x4, 0x76, 0xba }), + GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 }) , // Unique providerID {0905CA09-610E-401E-B650-2F212980B9E0} m_appLaunchActivity{ nullptr } { - // initialize the function array - InitFunctionLogArray(); + CoCreateGuid(&sessionGuid); } TraceLogger::~TraceLogger() @@ -125,29 +82,19 @@ namespace CalculatorApp } #pragma region Tracing methods - void TraceLogger::LogTelemetryEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY)); - } - - void TraceLogger::LogMeasureEvent(wstring_view eventName, LoggingFields fields) const - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_MEASURES)); - } - - void TraceLogger::LogCriticalDataEvent(wstring_view eventName, LoggingFields fields) const + void TraceLogger::LogLevel1Event(wstring_view eventName, LoggingFields fields) const { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_CRITICAL_DATA)); + g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_1)); } - void TraceLogger::LogPerformanceEvent(wstring_view eventName, LoggingFields fields) const + void TraceLogger::LogLevel2Event(wstring_view eventName, LoggingFields fields) const { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(WINEVENT_KEYWORD_RESPONSE_TIME)); + g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_2)); } - void TraceLogger::LogInfoEvent(wstring_view eventName, LoggingFields fields) const + void TraceLogger::LogLevel3Event(wstring_view eventName, LoggingFields fields) const { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Information); + g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_3)); } unique_ptr TraceLogger::CreateTraceActivity(wstring_view eventName, LoggingFields fields) const @@ -156,862 +103,283 @@ namespace CalculatorApp } #pragma endregion - void TraceLogger::InsertIntoMemoryMap(int windowId, bool isStandard, bool isScientific, bool isProgrammer) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - auto iterMap = s_memoryMap.find(windowId); - if (iterMap == s_memoryMap.end()) - { - s_memoryMap.insert(std::make_pair(windowId, vector())); - iterMap = s_memoryMap.find(windowId); - } - - if (isScientific) - { - iterMap->second.insert(iterMap->second.begin(), L"Scientific"); - } - else if (isProgrammer) - { - iterMap->second.insert(iterMap->second.begin(), L"Programmer"); - } - else if (isStandard) - { - iterMap->second.insert(iterMap->second.begin(), L"Standard"); - } - } - - void TraceLogger::UpdateMemoryMap(int windowId, int memoryPosition, bool isStandard, bool isScientific, bool isProgrammer) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - auto iterMap = s_memoryMap.find(windowId); - assert(iterMap != s_memoryMap.end()); - assert(iterMap->second.size() >= (unsigned int)memoryPosition); - - if (isScientific) - { - iterMap->second[memoryPosition] = L"Scientific"; - } - else if (isProgrammer) - { - iterMap->second[memoryPosition] = L"Programmer"; - } - else if (isStandard) - { - iterMap->second[memoryPosition] = L"Standard"; - } - } - - void TraceLogger::DeleteFromMemoryMap(int windowId, int memoryPosition) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - assert(iterMap != s_memoryMap.end()); - - iterMap->second.erase(iterMap->second.begin() + memoryPosition); - } - // return true if windowId is logged once else return false - bool TraceLogger::UpdateWindowIdLog(int windowId) + bool TraceLogger::IsWindowIdInLog(int windowId) { - // Writer lock for the static resources + // Writer lock for the windowIdLog resource reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - if (windowIdLog.find(windowId) == windowIdLog.end()) + if (find(windowIdLog.begin(), windowIdLog.end(), windowId) == windowIdLog.end()) { return false; } - if (windowIdLog[windowId] == false) - { - windowIdLog[windowId] = true; - return true; - } - else - { - return false; - } - } - - // call comes here at the time of ApplicationViewModel initialisation - void TraceLogger::LogCalculatorModeViewed(ViewMode mode, int windowId) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - // store windowId in windowIdLog which says we have logged mode for the present windowId. - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - windowIdLog.insert(pair(windowId, false)); - } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - - LoggingFields fields{}; - fields.AddString(L"CalculatorMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_CALCULATOR_VIEWED_IN_SESSION, fields); - } - } - - // call comes here at the time of ApplicationViewModel initialization - void TraceLogger::LogDateCalculatorModeViewed(ViewMode mode, int windowId) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - // store windowId in windowIdLog which says we have logged mode for the present windowId. - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - windowIdLog.insert(pair(windowId, false)); - } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - - LoggingFields fields{}; - fields.AddString(L"DateCalculatorMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_DATE_CALCULATOR_VIEWED_IN_SESSION, fields); - } - } - - // call comes here at the time of ApplicationViewModel initialization - void TraceLogger::LogConverterModeViewed(ViewMode mode, int windowId) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if (windowIdLog.find(windowId) == windowIdLog.end()) - { - windowIdLog.insert(pair(windowId, false)); - } - // if the event is not logged already for the present mode - if (currentMode != mode) - { - currentMode = mode; - - LoggingFields fields{}; - fields.AddString(L"ConverterMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_CONVERTER_VIEWED_IN_SESSION, fields); - } - } - - void TraceLogger::LogSharedMemoryUsed(wstring_view fromMode, wstring_view toMode, unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddString(L"FromMode", fromMode); - fields.AddString(L"ToMode", toMode); - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_SHARED_MEMORY_USED, fields); - } - - void TraceLogger::LogBitFlipPaneClicked() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_BITFLIP_PANE_CLICKED, fields); - } - - void TraceLogger::LogBitFlipUsed() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_BITFLIP_BUTTONS_USED, fields); - } - - void TraceLogger::LogAppLaunchStart() - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if (!isAppLaunchBeginLogged) - { - m_appLaunchActivity = - g_calculatorProvider.StartActivity(EVENT_NAME_APP_LAUNCH_BEGIN, nullptr, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_TELEMETRY)); - - isAppLaunchBeginLogged = true; - } - } - - void TraceLogger::LogAppLaunchComplete(/*Windows::ApplicationModel::Activation::ActivationKind activationKind, Windows::ApplicationModel::Activation::ApplicationExecutionState executionState*/) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if ((m_appLaunchActivity != nullptr) && (!isAppLaunchEndLogged)) - { - m_appLaunchActivity.StopActivity(EVENT_NAME_APP_LAUNCH_END); - - isAppLaunchEndLogged = true; - } - - m_appLaunchActivity = nullptr; - } - - void TraceLogger::LogAppResumeComplete() - { - if (m_appLaunchActivity != nullptr) - { - m_appLaunchActivity.StopActivity(EVENT_NAME_APP_RESUME_END); - } - m_appLaunchActivity = nullptr; + return true; } - void TraceLogger::LogDebug(wstring_view debugData) + void TraceLogger::LogVisualStateChanged(ViewMode mode, wstring_view state, bool isAlwaysOnTop) const { if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddString(L"DebugData", debugData); - LogTelemetryEvent(EVENT_NAME_DEBUG, fields); - } - - void TraceLogger::LogOnAppLaunch(wstring_view previousExecutionState) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddString(L"PreviousExecutionState", previousExecutionState); - LogTelemetryEvent(EVENT_NAME_PREVIOUS_STATE_WINDOW_ON_CREATION, fields); - } - - void TraceLogger::LogAboutFlyoutOpened() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_ABOUT_FLYOUT_OPENED, fields); - } - - void TraceLogger::LogNavBarOpened() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_NAV_BAR_OPENED, fields); - } - - void TraceLogger::LogClearHistory() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_CLEAR, fields); - } - - void TraceLogger::LogHistoryFlyoutOpenBegin(unsigned int historyItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - // we want to record the event only when history item count is atleast 20 - if (historyItemCount >= 20) { - LoggingFields fields{}; - fields.AddUInt32(L"HistoryItemCount", historyItemCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_BEGIN, fields); - } - } - - void TraceLogger::LogHistoryFlyoutOpenEnd(int historyItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - if (historyItemCount >= 20) - { - LoggingFields fields{}; - fields.AddUInt32(L"HistoryItemCount", historyItemCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_FLYOUT_OPEN_END, fields); } - } - - void TraceLogger::LogHistoryBodyOpened() const - { - if (!GetTraceLoggingProviderEnabled()) - return; LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_BODY_OPENED, fields); - } - - void TraceLogger::LogMemoryFlyoutOpenBegin(unsigned int memoryItemCount) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - // we want to record the event only when memory item count is atleast 4 - if (memoryItemCount >= 4) - { - LoggingFields fields{}; - fields.AddUInt32(L"MemoryItemCount", memoryItemCount); - LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_BEGIN, fields); - } + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddString(L"VisualState", state); + fields.AddBoolean(L"IsAlwaysOnTop", isAlwaysOnTop); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_VISUAL_STATE_CHANGED, fields); } - void TraceLogger::LogMemoryFlyoutOpenEnd(unsigned int memoryItemCount) const + void TraceLogger::LogWindowCreated(ViewMode mode, int windowId) { - if (!GetTraceLoggingProviderEnabled()) - return; - - if (memoryItemCount >= 4) + // store windowId in windowIdLog which says we have logged mode for the present windowId. + if (!IsWindowIdInLog(windowId)) { - LoggingFields fields{}; - fields.AddUInt32(L"MemoryItemCount", memoryItemCount); - LogTelemetryEvent(EVENT_NAME_MEMORY_FLYOUT_OPEN_END, fields); + windowIdLog.push_back(windowId); } - } - void TraceLogger::LogMemoryBodyOpened() const - { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_MEMORY_BODY_OPENED, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddUInt64(L"NumOfOpenWindows", currentWindowCount); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_WINDOW_ON_CREATED, fields); } - // If calculator is launched in any mode other than standard then this call will come which is not intended. But there is no way to avoid it. - // So don't use this function to analyze the count of mode change in session instead use CalculatorViewedInSession and ConverterViewedInSession to do that. - // Use of this function is to analyze perf of mode change. - void TraceLogger::LogModeChangeBegin(ViewMode fromMode, ViewMode toMode, int windowId) + void TraceLogger::LogModeChange(ViewMode mode) const { if (!GetTraceLoggingProviderEnabled()) return; - if (NavCategory::IsValidViewMode(fromMode) && NavCategory::IsValidViewMode(toMode)) + if (NavCategory::IsValidViewMode(mode)) { LoggingFields fields{}; - fields.AddString(L"FromMode", NavCategory::GetFriendlyName(fromMode)->Data()); - fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data()); - fields.AddInt32(L"WindowId", windowId); - LogMeasureEvent(EVENT_NAME_MODE_CHANGE_BEGIN, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_MODE_CHANGED, fields); } } - // comment: same as LogModeChangeBegin - void TraceLogger::LogModeChangeEnd(ViewMode toMode, int windowId) const + void TraceLogger::LogHistoryItemLoad(ViewMode mode, int historyListSize, int loadedIndex) const { if (!GetTraceLoggingProviderEnabled()) - return; - - if (NavCategory::IsValidViewMode(toMode)) { - LoggingFields fields{}; - fields.AddString(L"ToMode", NavCategory::GetFriendlyName(toMode)->Data()); - fields.AddInt32(L"WindowId", windowId); - LogMeasureEvent(EVENT_NAME_MODE_CHANGE_END, fields); - } - } - - void TraceLogger::LogHistoryItemLoadBegin() const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_BEGIN, fields); - } - - void TraceLogger::LogHistoryItemLoadEnd(unsigned int historyTokenCount) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"HistoryTokenCount", historyTokenCount); - LogTelemetryEvent(EVENT_NAME_HISTORY_ITEM_LOAD_END, fields); - } - - void TraceLogger::LogNewWindowCreationBegin(int windowId) - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_BEGIN, fields); - } - - void TraceLogger::LogNewWindowCreationEnd(int windowId) - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_NEW_WINDOW_CREATION_END, fields); - } - - void TraceLogger::LogError(wstring_view errorString) - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddString(L"ErrorString", errorString); - LogTelemetryEvent(EVENT_NAME_ERROR, fields); - } - - void TraceLogger::LogPrelaunchedAppActivatedByUser() - { - if (!GetTraceLoggingProviderEnabled()) return; + } LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_PRELAUNCHED_APP_ACTIVATED_BY_USER, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddInt32(L"HistoryListSize", historyListSize); + fields.AddInt32(L"HistoryItemIndex", loadedIndex); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_HISTORY_ITEM_LOAD, fields); } - void TraceLogger::LogAppPrelaunchedBySystem() + void TraceLogger::LogMemoryItemLoad(ViewMode mode, int memoryListSize, int loadedIndex) const { if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_APP_PRELAUNCHED_BY_SYSTEM, fields); - } - - void TraceLogger::LogMemoryClearAll(int windowId) - { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_MEMORY_CLEAR_ALL, fields); - - if (iterMap != s_memoryMap.end()) { - iterMap->second.clear(); - } - } - - void - TraceLogger::LogMemoryUsed(int windowId, unsigned int slotPosition, bool isStandard, bool isScientific, bool isProgrammer, unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) return; - - // Reader lock for the static resources - reader_writer_lock::scoped_lock_read lock(s_traceLoggerLock); - auto iterMap = s_memoryMap.find(windowId); - - if (slotPosition == 0) - { - LogSingleMemoryUsed(memorySize); - } - else - { - LogMultipleMemoryUsed(slotPosition, memorySize); } - if (iterMap != s_memoryMap.end()) - { - // if current mode is not equal to mode of memoryMap[slotPosition] then LogSharedMemoryUsed - if (isStandard && (iterMap->second[slotPosition] != L"Standard")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Standard", memorySize); - } - else if (isScientific && (iterMap->second[slotPosition] != L"Scientific")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Scientific", memorySize); - } - else if (isProgrammer && (iterMap->second[slotPosition] != L"Programmer")) - { - LogSharedMemoryUsed(iterMap->second[slotPosition], L"Programmer", memorySize); - } - } - } - - void TraceLogger::LogMultipleMemoryUsed(unsigned int slotPosition, unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"MemoryIndex", slotPosition); - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_MULTIPLE_MEMORY_USED, fields); - } - - void TraceLogger::LogSingleMemoryUsed(unsigned int memorySize) const - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"MemoryListSize", memorySize); - LogTelemetryEvent(EVENT_NAME_SINGLE_MEMORY_USED, fields); - } - - void TraceLogger::LogInvalidPastedInputOccurred(wstring_view reason, ViewMode mode, int programmerNumberBase, int bitLengthType) - { - if (!GetTraceLoggingProviderEnabled()) - return; - LoggingFields fields{}; - fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddString(L"Reason", reason); - fields.AddString(L"ProgrammerNumberBase", GetProgrammerType(programmerNumberBase).c_str()); - fields.AddString(L"BitLengthType", GetProgrammerType(bitLengthType).c_str()); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddInt32(L"MemoryListSize", memoryListSize); + fields.AddInt32(L"MemoryItemIndex", loadedIndex); fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogTelemetryEvent(EVENT_NAME_INVALID_PASTED_INPUT_OCCURRED, fields); + LogLevel2Event(EVENT_NAME_MEMORY_ITEM_LOAD, fields); } - void TraceLogger::LogValidInputPasted(ViewMode mode) const + void TraceLogger::LogError(ViewMode mode, wstring_view functionName, wstring_view errorString) { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; - fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data()); - LogTelemetryEvent(EVENT_NAME_VALID_INPUT_PASTED, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddString(L"FunctionName", functionName); + fields.AddString(L"Message", errorString); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_EXCEPTION, fields); } - void TraceLogger::LogStandardException(wstring_view functionName, const exception& e) const + void TraceLogger::LogStandardException(ViewMode mode, wstring_view functionName, const exception& e) const { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); fields.AddString(L"FunctionName", functionName); wstringstream exceptionMessage; exceptionMessage << e.what(); - fields.AddString(L"ExceptionMessage", exceptionMessage.str()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + fields.AddString(L"Message", exceptionMessage.str()); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_EXCEPTION, fields); } - void TraceLogger::LogWinRTException(wstring_view functionName, hresult_error const& e) const + void TraceLogger::LogWinRTException(ViewMode mode, wstring_view functionName, hresult_error const& e) const { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); fields.AddString(L"FunctionName", functionName); + fields.AddString(L"Message", e.message()); fields.AddInt32(L"HRESULT", e.code()); - fields.AddString(L"ExceptionMessage", e.message()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_EXCEPTION, fields); } - void TraceLogger::LogPlatformException(wstring_view functionName, Platform::Exception ^ e) const + void TraceLogger::LogPlatformException(ViewMode mode, wstring_view functionName, Platform::Exception ^ e) const { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); fields.AddString(L"FunctionName", functionName); + fields.AddString(L"Message", e->Message->Data()); fields.AddInt32(L"HRESULT", e->HResult); - fields.AddString(L"ExceptionMessage", e->Message->Data()); - LogMeasureEvent(EVENT_NAME_EXCEPTION, fields); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_EXCEPTION, fields); } - void TraceLogger::UpdateFunctionUsage(int funcIndex) + void TraceLogger::UpdateButtonUsage(NumbersAndOperatorsEnum button, ViewMode mode) { - // Writer lock for the static resources - reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - - if (GetIndex(funcIndex)) + // IsProgrammerMode, IsScientificMode, IsStandardMode and None are not actual buttons, so ignore them + if (button == NumbersAndOperatorsEnum::IsProgrammerMode || button == NumbersAndOperatorsEnum::IsScientificMode + || button == NumbersAndOperatorsEnum::IsStandardMode || button == NumbersAndOperatorsEnum::None) { - // funcIndex is passed by reference and will be having the returned index - funcLog[funcIndex].count++; + return; } - } - void TraceLogger::InitFunctionLogArray() - { - int i = -1; - for (int funcIndex = 0; funcIndex != maxFunctionSize; funcIndex++) { - FunctionLogEnum func = safe_cast(funcIndex); - wstring functionName = func.ToString()->Data(); - if (functionName.compare(L"CalculatorApp.FunctionLogEnum") != 0) + // Writer lock for the buttonLog resource + reader_writer_lock::scoped_lock lock(s_traceLoggerLock); + + vector::iterator it = std::find_if( + buttonLog.begin(), buttonLog.end(), [button, mode](const ButtonLog& bLog) -> bool { return bLog.button == button && bLog.mode == mode; }); + if (it != buttonLog.end()) { - findIndex[funcIndex] = ++i; - funcLog.push_back(FuncLog(functionName)); + it->count++; + } + else + { + buttonLog.push_back(ButtonLog(button, mode)); } } - // update the functionCount with total function count which we are tracking through tracelog. - functionCount = i; - } - - wstring TraceLogger::GetProgrammerType(int index) - { - if (index >= 0) - { - return s_programmerType[index]; - } - // return "N/A" - return s_programmerType[0]; - } - bool TraceLogger::GetIndex(int& index) - { - if (findIndex[index] > 0) + // Periodically log the button usage so that we do not lose all button data if the app is foricibly closed or crashes + if (buttonLog.size() >= 10) { - index = findIndex[index]; - return true; + LogButtonUsage(); } - return false; } void TraceLogger::UpdateWindowCount(size_t windowCount) { - maxWindowCount = (maxWindowCount > windowCount) ? maxWindowCount : windowCount; - windowLaunchCount++; - } - - void TraceLogger::LogMaxWindowCount() - { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddUInt32(L"WindowCount", (unsigned int)maxWindowCount); - LogTelemetryEvent(EVENT_NAME_MAX_WINDOW_COUNT, fields); - } - - void TraceLogger::LogWindowActivated() const - { - if (!GetTraceLoggingProviderEnabled()) + if (windowCount == 0) + { + currentWindowCount--; return; - - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_PROTOCOL, fields); + } + currentWindowCount = windowCount; } - void TraceLogger::LogWindowLaunched() const + void TraceLogger::LogButtonUsage() { if (!GetTraceLoggingProviderEnabled()) return; - LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_WINDOW_LAUNCHED_TILESEARCH, fields); - } - - void TraceLogger::LogBitLengthButtonUsed(int windowId) - { - if (bitLengthButtonUsage.find(windowId) == bitLengthButtonUsage.end()) - { - bitLengthButtonUsage.insert(pair(windowId, 1)); - } - else - { - bitLengthButtonUsage[windowId]++; - } - if ((bitLengthButtonUsage[windowId] == 5) && !bitLengthButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_BITLENGTH_BUTTON_USED, fields); - - bitLengthButtonLoggedInSession = true; - } - } - - void TraceLogger::LogRadixButtonUsed(int windowId) - { - if (radixButtonUsage.find(windowId) == radixButtonUsage.end()) - { - radixButtonUsage.insert(pair(windowId, 1)); - } - else - { - radixButtonUsage[windowId]++; - } - if ((radixButtonUsage[windowId] == 2) && !radixButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_RADIX_BUTTON_USED, fields); - - radixButtonLoggedInSession = true; - } - } + // Writer lock for the buttonLog resource + reader_writer_lock::scoped_lock lock(s_traceLoggerLock); - void TraceLogger::LogAngleButtonUsed(int windowId) - { - if (angleButtonUsage.find(windowId) == angleButtonUsage.end()) + if (buttonLog.size() == 0) { - angleButtonUsage.insert(pair(windowId, 1)); - } - else - { - angleButtonUsage[windowId]++; - } - if ((angleButtonUsage[windowId] == 2) && !angleButtonLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_ANGLE_BUTTONS_USED, fields); - - angleButtonLoggedInSession = true; - } - } - - void TraceLogger::LogFunctionUsage(int windowId) - { - if (!GetTraceLoggingProviderEnabled()) return; + } - for (int i = 0; i < functionCount; i++) + Platform::String ^ buttonUsageString; + for (size_t i = 0; i < buttonLog.size(); i++) { - // log only those functions which are used - if (funcLog[i].count > 0) + buttonUsageString += NavCategory::GetFriendlyName(buttonLog[i].mode); + buttonUsageString += "|"; + buttonUsageString += buttonLog[i].button.ToString(); + buttonUsageString += "|"; + buttonUsageString += buttonLog[i].count; + if (i != buttonLog.size() - 1) { - LoggingFields fields{}; - fields.AddString(L"FunctionName", funcLog[i].funcName.data()); - fields.AddUInt32(L"UsageCount", funcLog[i].count); - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_FUNCTION_USAGE, fields); + buttonUsageString += ","; } } - } - - void TraceLogger::LogHypButtonUsed(int windowId) - { - if (!isHypButtonLogged) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_HYP_BUTTON_USED, fields); - - isHypButtonLogged = true; - } - } - - void TraceLogger::LogDateDifferenceModeUsed(int windowId) - { - if (!m_dateDiffUsageLoggedInSession) - { - LoggingFields fields{}; - fields.AddUInt32(L"WindowId", windowId); - LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_USED, fields); - - m_dateDiffUsageLoggedInSession = true; - } - } - - void TraceLogger::LogDateAddSubtractModeUsed(int windowId, bool isAddMode) - { - auto usageMap = isAddMode ? &m_dateAddModeUsage : &m_dateSubtractModeUsage; - auto isLoggedInSession = isAddMode ? &m_dateAddUsageLoggedInSession : &m_dateSubtractUsageLoggedInSession; - - if (usageMap->find(windowId) == usageMap->end()) - { - usageMap->insert(pair(windowId, 1)); - } - else - { - (*usageMap)[windowId]++; - } - - // Ignore first 3 calls during the initialization of the combo box selected items for Add mode - int firstChangeEventCount = isAddMode ? 4 : 1; - if (((*usageMap)[windowId] == firstChangeEventCount) && (!(*isLoggedInSession))) - { - LoggingFields fields{}; - fields.AddString(L"AddSubtractMode", isAddMode ? L"Add" : L"Subtract"); - LogTelemetryEvent(EVENT_NAME_DATE_ADD_SUBTRACT_USED, fields); + LoggingFields fields{}; + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"ButtonUsage", buttonUsageString->Data()); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_BUTTON_USAGE, fields); - *isLoggedInSession = true; - } + buttonLog.clear(); } - void TraceLogger::LogDateClippedTimeDifferenceFound(Calendar const& today, DateTime const& clippedTime) const + void TraceLogger::LogDateCalculationModeUsed(bool AddSubtractMode) { - if (!GetTraceLoggingProviderEnabled()) - return; - - auto calendarSystem = today.GetCalendarSystem(); - auto clock = today.GetClock(); - DateTimeFormatter dtFormatter{ L"longdate shorttime", { L"en-US" }, GlobalizationPreferences::HomeGeographicRegion(), calendarSystem, clock }; - + const wchar_t* calculationType = AddSubtractMode ? L"AddSubtractMode" : L"DateDifferenceMode"; LoggingFields fields{}; - fields.AddString(L"ResolvedCalendarLanguage", today.ResolvedLanguage()); - fields.AddString(L"Timezone", today.GetTimeZone()); - fields.AddString(L"CalendarSystem", calendarSystem); - fields.AddString(L"Clock", clock); - fields.AddBoolean(L"IsDaylightSavingTime", today.IsDaylightSavingTime()); - fields.AddString(L"TodayDate", dtFormatter.Format(today.GetDateTime())); - fields.AddString(L"ClippedDate", dtFormatter.Format(clippedTime)); - LogTelemetryEvent(EVENT_NAME_DATE_DIFFERENCE_FOUND, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(ViewMode::Date)->Data()); + fields.AddString(L"CalculationType", calculationType); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_DATE_CALCULATION_MODE_USED, fields); } - void TraceLogger::LogUserRequestedRefreshFailed() const + void TraceLogger::LogConverterInputReceived(ViewMode mode) const { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; - LogTelemetryEvent(L"UserRequestedRefreshFailed", fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_CONVERTER_INPUT_RECEIVED, fields); } - void TraceLogger::LogConversionResult(wstring_view fromValue, wstring_view fromUnit, wstring_view toValue, wstring_view toUnit) const + void TraceLogger::LogNavBarOpened() const { if (!GetTraceLoggingProviderEnabled()) return; - wstring behaviorString{}; - NetworkAccessBehavior behavior = NetworkManager::GetNetworkAccessBehavior(); - switch (behavior) - { - case NetworkAccessBehavior::Offline: - behaviorString = L"Offline"; - break; - - case NetworkAccessBehavior::OptIn: - behaviorString = L"Metered"; - break; - - case NetworkAccessBehavior::Normal: - default: - behaviorString = L"Online"; - break; - } - LoggingFields fields{}; - fields.AddString(L"NetworkAccess", behaviorString); - fields.AddString(L"FromValue", fromValue); - fields.AddString(L"FromUnit", fromUnit); - fields.AddString(L"ToValue", toValue); - fields.AddString(L"ToUnit", toUnit); - LogTelemetryEvent(L"CurrencyConverterInputReceived", fields); - } - - void TraceLogger::LogViewClosingTelemetry(int windowId) - { - LogFunctionUsage(windowId); - LogMaxWindowCount(); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_NAV_BAR_OPENED, fields); } - void TraceLogger::LogCoreWindowWasNull() const + void TraceLogger::LogInputPasted(ViewMode mode) const { if (!GetTraceLoggingProviderEnabled()) return; LoggingFields fields{}; - LogTelemetryEvent(EVENT_NAME_CORE_WINDOW_WAS_NULL, fields); + fields.AddGuid(L"SessionGuid", sessionGuid); + fields.AddString(L"Mode", NavCategory::GetFriendlyName(mode)->Data()); + fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); + LogLevel2Event(EVENT_NAME_INPUT_PASTED, fields); } } diff --git a/src/CalcViewModel/Common/TraceLogger.h b/src/CalcViewModel/Common/TraceLogger.h index 870cddf8d..d8a4289ee 100644 --- a/src/CalcViewModel/Common/TraceLogger.h +++ b/src/CalcViewModel/Common/TraceLogger.h @@ -6,6 +6,7 @@ #include "CalcManager/Command.h" #include "TraceActivity.h" #include "NavCategory.h" +#include "CalculatorButtonUser.h" static const int maxFunctionSize = (int)CalculationManager::Command::CommandBINEDITEND; @@ -13,19 +14,17 @@ static const int maxFunctionSize = (int)CalculationManager::Command::CommandBINE // This class implements a singleton model ensure that only one instance is created. namespace CalculatorApp { - struct FuncLog + struct ButtonLog { public: int count; - std::wstring funcName; - FuncLog() + CalculatorApp::NumbersAndOperatorsEnum button; + CalculatorApp::Common::ViewMode mode; + ButtonLog(CalculatorApp::NumbersAndOperatorsEnum btn, CalculatorApp::Common::ViewMode vMode) { - count = 0; - } - FuncLog(std::wstring fName) - { - funcName = fName; - count = 0; + button = btn; + mode = vMode; + count = 1; } }; @@ -38,123 +37,46 @@ namespace CalculatorApp static TraceLogger& GetInstance(); bool GetTraceLoggingProviderEnabled() const; - void LogAppLaunchStart(); - void LogAppLaunchComplete(); - void LogAppResumeComplete(); - void LogOnAppLaunch(std::wstring_view previousExecutionState) const; - void LogMemoryClearAll(int); - void LogBitFlipPaneClicked() const; - void LogBitFlipUsed() const; - void LogHistoryBodyOpened() const; - void LogHistoryItemLoadBegin() const; - void LogHistoryItemLoadEnd(unsigned int) const; - void LogHistoryFlyoutOpenBegin(unsigned int) const; - void LogHistoryFlyoutOpenEnd(int) const; - void LogCalculatorModeViewed(CalculatorApp::Common::ViewMode, int); - void LogDateCalculatorModeViewed(CalculatorApp::Common::ViewMode, int); - void LogConverterModeViewed(CalculatorApp::Common::ViewMode, int); - void LogModeChangeBegin(CalculatorApp::Common::ViewMode, CalculatorApp::Common::ViewMode, int); - void LogModeChangeEnd(CalculatorApp::Common::ViewMode, int) const; - void LogClearHistory() const; - void InsertIntoMemoryMap(int, bool, bool, bool); - void UpdateMemoryMap(int, int, bool, bool, bool); - void DeleteFromMemoryMap(int, int); - void LogMemoryUsed(int, unsigned int, bool, bool, bool, unsigned int) const; - void LogMultipleMemoryUsed(unsigned int, unsigned int) const; - void LogSingleMemoryUsed(unsigned int) const; - void LogSharedMemoryUsed(std::wstring_view, std::wstring_view, unsigned int) const; - void LogMemoryBodyOpened() const; - void LogMemoryFlyoutOpenBegin(unsigned int) const; - void LogDebug(std::wstring_view debugData); - void LogMemoryFlyoutOpenEnd(unsigned int) const; - void LogInvalidPastedInputOccurred(std::wstring_view reason, CalculatorApp::Common::ViewMode mode, int ProgrammerNumberBase, int bitLengthType); - void LogValidInputPasted(CalculatorApp::Common::ViewMode mode) const; - void UpdateFunctionUsage(int func); - void LogFunctionUsage(int); - void InitFunctionLogArray(); - void LogBitLengthButtonUsed(int windowId); - void LogRadixButtonUsed(int windowId); - void LogAngleButtonUsed(int windowId); - void LogHypButtonUsed(int windowId); - void LogNewWindowCreationBegin(int windowId); - void LogNewWindowCreationEnd(int windowId); - void LogError(std::wstring_view errorString); - void LogPrelaunchedAppActivatedByUser(); - void LogAppPrelaunchedBySystem(); - void UpdateWindowCount(size_t windowCount); - bool UpdateWindowIdLog(int windowId); - void LogMaxWindowCount(); - void LogWindowActivated() const; - void LogWindowLaunched() const; - void LogUserRequestedRefreshFailed() const; - void LogConversionResult(std::wstring_view fromValue, std::wstring_view fromUnit, std::wstring_view toValue, std::wstring_view toUnit) const; - void LogAboutFlyoutOpened() const; + void LogModeChange(CalculatorApp::Common::ViewMode mode) const; + void LogHistoryItemLoad(CalculatorApp::Common::ViewMode mode, int historyListSize, int loadedIndex) const; + void LogMemoryItemLoad(CalculatorApp::Common::ViewMode mode, int memoryListSize, int loadedIndex) const; + void UpdateButtonUsage(CalculatorApp::NumbersAndOperatorsEnum button, CalculatorApp::Common::ViewMode mode); + void LogButtonUsage(); + void LogDateCalculationModeUsed(bool AddSubtractMode); + void UpdateWindowCount(size_t windowCount = 0); + bool IsWindowIdInLog(int windowId); + void LogVisualStateChanged(CalculatorApp::Common::ViewMode mode, std::wstring_view state, bool isAlwaysOnTop = false) const; + void LogWindowCreated(CalculatorApp::Common::ViewMode mode, int windowId); + void LogConverterInputReceived(CalculatorApp::Common::ViewMode mode) const; void LogNavBarOpened() const; - void LogViewClosingTelemetry(int); - void LogCoreWindowWasNull() const; - - // Trace methods for Date Calculator usage - void LogDateDifferenceModeUsed(int windowId); - void LogDateAddSubtractModeUsed(int windowId, bool isAddMode); - void - LogDateClippedTimeDifferenceFound(winrt::Windows::Globalization::Calendar const& today, winrt::Windows::Foundation::DateTime const& clippedTime) const; - void LogStandardException(std::wstring_view functionName, _In_ const std::exception& e) const; - void LogWinRTException(std::wstring_view functionName, _In_ winrt::hresult_error const& e) const; - void LogPlatformException(std::wstring_view functionName, _In_ Platform::Exception ^ e) const; + void LogError(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, std::wstring_view errorString); + void LogStandardException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ const std::exception& e) const; + void LogWinRTException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ winrt::hresult_error const& e) const; + void LogPlatformException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ Platform::Exception ^ e) const; + void LogInputPasted(CalculatorApp::Common::ViewMode mode) const; private: // Create an instance of TraceLogger TraceLogger(); - // Any new Log method should - // a) decide the level of logging. This will help us in limiting recording of events only up to a certain level. See this link for guidance - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363742(v=vs.85).aspx We're using Verbose level for events that are called frequently and - // needed only for debugging or capturing perf for specific scenarios b) should decide whether or not to log to telemetry and pass - // TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY) accordingly c) Should accept a variable number of additional data arguments if needed - void LogTelemetryEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogMeasureEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogCriticalDataEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogPerformanceEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; - void LogInfoEvent(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; + // As mentioned in Microsoft's Privacy Statement(https://privacy.microsoft.com/en-US/privacystatement#maindiagnosticsmodule), + // sampling is involved in Microsoft's diagnostic data collection process. + // These keywords provide additional input into how frequently an event might be sampled. + // The lower the level of the keyword, the higher the possibility that the corresponding event may be sampled. + void LogLevel1Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; + void LogLevel2Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; + void LogLevel3Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; std::unique_ptr CreateTraceActivity(std::wstring_view activityName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields) const; winrt::Windows::Foundation::Diagnostics::LoggingChannel g_calculatorProvider; - bool isSizeChangeLogged = false; - bool isHideIfShownLogged = false; - bool isSizeChangedFirstTime = true; // to track the first size changed event which is fired on the launch of app - bool isAutoConversionBeginLoggedInSession = false; - bool isAutoConversionEndLoggedInSession = false; - bool angleButtonLoggedInSession = false; - bool radixButtonLoggedInSession = false; - bool bitLengthButtonLoggedInSession = false; - GUID sessionGuid; - CalculatorApp::Common::ViewMode currentMode = CalculatorApp::Common::ViewMode::None; - std::vector funcLog; - int functionCount = 0; - bool isHypButtonLogged = false; - bool isAngleButtonInitialized = false; - unsigned int findIndex[maxFunctionSize] = { 0 }; - bool GetIndex(int& index); - std::wstring GetProgrammerType(int index); - size_t maxWindowCount = 0; - bool isAppLaunchBeginLogged = false; - bool isAppLaunchEndLogged = false; - std::map bitLengthButtonUsage; - std::map angleButtonUsage; - std::map radixButtonUsage; - std::map windowIdLog; - - // Private variables for Date Calculator usage - bool m_dateDiffUsageLoggedInSession = false; - bool m_dateAddUsageLoggedInSession = false; - bool m_dateSubtractUsageLoggedInSession = false; - std::map m_dateAddModeUsage; - std::map m_dateSubtractModeUsage; + std::vector buttonLog; + std::vector windowIdLog; - size_t windowLaunchCount = 0; + GUID sessionGuid; + size_t currentWindowCount = 0; winrt::Windows::Foundation::Diagnostics::LoggingActivity m_appLaunchActivity; }; diff --git a/src/CalcViewModel/Common/Utils.h b/src/CalcViewModel/Common/Utils.h index 25098a290..df2788c19 100644 --- a/src/CalcViewModel/Common/Utils.h +++ b/src/CalcViewModel/Common/Utils.h @@ -416,8 +416,12 @@ namespace Utils Windows::Foundation::DateTime GetUniversalSystemTime(); bool IsDateTimeOlderThan(Windows::Foundation::DateTime dateTime, const long long duration); - concurrency::task WriteFileToFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName, Platform::String^ contents, Windows::Storage::CreationCollisionOption collisionOption); - concurrency::task ReadFileFromFolder(Windows::Storage::IStorageFolder^ folder, Platform::String^ fileName); + concurrency::task WriteFileToFolder( + Windows::Storage::IStorageFolder ^ folder, + Platform::String ^ fileName, + Platform::String ^ contents, + Windows::Storage::CreationCollisionOption collisionOption); + concurrency::task ReadFileFromFolder(Windows::Storage::IStorageFolder ^ folder, Platform::String ^ fileName); bool AreColorsEqual(const Windows::UI::Color& color1, const Windows::UI::Color& color2); diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp index 2e7cc3ccc..5180de47e 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.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" @@ -39,7 +39,9 @@ static constexpr auto CURRENCY_UNIT_TO_KEY = L"CURRENCY_UNIT_TO_KEY"; static constexpr long long DAY_DURATION = 1LL * 60 * 60 * 24 * 10000000; static constexpr long long WEEK_DURATION = DAY_DURATION * 7; -static constexpr int FORMATTER_DIGIT_COUNT = 4; +static constexpr int FORMATTER_RATE_FRACTION_PADDING = 2; +static constexpr int FORMATTER_RATE_MIN_DECIMALS = 4; +static constexpr int FORMATTER_RATE_MIN_SIGNIFICANT_DECIMALS = 4; static constexpr auto CACHE_TIMESTAMP_KEY = L"CURRENCY_CONVERTER_TIMESTAMP"; static constexpr auto CACHE_LANGCODE_KEY = L"CURRENCY_CONVERTER_LANGCODE"; @@ -128,7 +130,7 @@ CurrencyDataLoader::CurrencyDataLoader(_In_ unique_ptr clie m_ratioFormatter = localizationService->GetRegionalSettingsAwareDecimalFormatter(); m_ratioFormatter->IsGrouped = true; m_ratioFormatter->IsDecimalPointAlwaysDisplayed = true; - m_ratioFormatter->FractionDigits = FORMATTER_DIGIT_COUNT; + m_ratioFormatter->FractionDigits = FORMATTER_RATE_FRACTION_PADDING; m_ratioFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyFromToRatioFormat")->Data(); m_timestampFormat = AppResourceProvider::GetInstance().GetResourceString(L"CurrencyTimestampFormat")->Data(); @@ -267,6 +269,23 @@ pair CurrencyDataLoader::GetCurrencySymbols(const UCM::Unit& u return make_pair(symbol1, symbol2); } +double CurrencyDataLoader::RoundCurrencyRatio(double ratio) +{ + // Compute how many decimals we need to display two meaningful digits at minimum + // For example: 0.00000000342334 -> 0.000000003423, 0.000212 -> 0.000212 + int numberDecimals = FORMATTER_RATE_MIN_DECIMALS; + if (ratio < 1) + { + numberDecimals = max( + FORMATTER_RATE_MIN_DECIMALS, + (int)(-log10(ratio)) + FORMATTER_RATE_MIN_SIGNIFICANT_DECIMALS); + } + + unsigned long long scale = (unsigned long long)powl(10l, numberDecimals); + + return (double)(round(ratio * scale) / scale); +} + pair CurrencyDataLoader::GetCurrencyRatioEquality(_In_ const UCM::Unit& unit1, _In_ const UCM::Unit& unit2) { try @@ -279,12 +298,7 @@ pair CurrencyDataLoader::GetCurrencyRatioEquality(_In_ const U if (iter2 != ratioMap.end()) { double ratio = (iter2->second).ratio; - - // Round the ratio to FORMATTER_DIGIT_COUNT digits using int math. - // Ex: to round 1.23456 to three digits, use - // ((int) 1.23456 * (10^3)) / (10^3) - double scale = pow(10, FORMATTER_DIGIT_COUNT); - double rounded = static_cast(ratio * static_cast(scale)) / scale; + double rounded = RoundCurrencyRatio(ratio); wstring digitSymbol = wstring{ LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'1') }; wstring roundedFormat = m_ratioFormatter->Format(rounded)->Data(); @@ -335,12 +349,12 @@ future CurrencyDataLoader::TryLoadDataFromCacheAsync() } catch (Exception ^ ex) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + TraceLogger::GetInstance().LogPlatformException(ViewMode::Currency, __FUNCTIONW__, ex); co_return false; } catch (const exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance().LogStandardException(ViewMode::Currency, __FUNCTIONW__, e); co_return false; } catch (...) @@ -445,12 +459,12 @@ future CurrencyDataLoader::TryLoadDataFromWebAsync() } catch (Exception ^ ex) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + TraceLogger::GetInstance().LogPlatformException(ViewMode::Currency, __FUNCTIONW__, ex); co_return false; } catch (const exception& e) { - TraceLogger::GetInstance().LogStandardException(__FUNCTIONW__, e); + TraceLogger::GetInstance().LogStandardException(ViewMode::Currency, __FUNCTIONW__, e); co_return false; } catch (...) @@ -466,7 +480,7 @@ future CurrencyDataLoader::TryLoadDataFromWebOverrideAsync() if (!didLoad) { m_loadStatus = CurrencyLoadStatus::FailedToLoad; - TraceLogger::GetInstance().LogUserRequestedRefreshFailed(); + TraceLogger::GetInstance().LogError(ViewMode::Currency, L"CurrencyDataLoader::TryLoadDataFromWebOverrideAsync", L"UserRequestedRefreshFailed"); } co_return didLoad; @@ -527,8 +541,11 @@ bool CurrencyDataLoader::TryParseStaticData(_In_ String ^ rawJson, _Inout_ vecto staticData[i] = CurrencyStaticData{ countryCode, countryName, currencyCode, currencyName, currencySymbol }; } - // TODO - MSFT 8533667: this sort will be replaced by a WinRT call to sort localized strings - sort(begin(staticData), end(staticData), [](CurrencyStaticData unit1, CurrencyStaticData unit2) { return unit1.countryName < unit2.countryName; }); + auto sortCountryNames = [](const UCM::CurrencyStaticData & s) { + return ref new String(s.countryName.c_str()); + }; + + LocalizationService::GetInstance()->Sort(staticData, sortCountryNames); return true; } diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h index a4be2d0e6..7a28c6c71 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h @@ -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 @@ -75,6 +75,7 @@ namespace CalculatorApp std::pair GetCurrencyRatioEquality(_In_ const UnitConversionManager::Unit& unit1, _In_ const UnitConversionManager::Unit& unit2) override; std::wstring GetCurrencyTimestamp() override; + static double RoundCurrencyRatio(double ratio); std::future TryLoadDataFromCacheAsync() override; std::future TryLoadDataFromWebAsync() override; diff --git a/src/CalcViewModel/DateCalculatorViewModel.cpp b/src/CalcViewModel/DateCalculatorViewModel.cpp index 471d5df82..e89c136a9 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.cpp +++ b/src/CalcViewModel/DateCalculatorViewModel.cpp @@ -88,9 +88,6 @@ DateCalculatorViewModel::DateCalculatorViewModel() if (calendar->DayOfWeek != trueDayOfWeek) { calendar->SetDateTime(today); - TraceLogger::GetInstance().LogDateClippedTimeDifferenceFound( - from_cx(calendar), - winrt::Windows::Foundation::DateTime{ winrt::Windows::Foundation::TimeSpan{ clippedTime.UniversalTime } }); } } diff --git a/src/CalcViewModel/HistoryViewModel.cpp b/src/CalcViewModel/HistoryViewModel.cpp index 703742529..8da5007ac 100644 --- a/src/CalcViewModel/HistoryViewModel.cpp +++ b/src/CalcViewModel/HistoryViewModel.cpp @@ -118,6 +118,9 @@ void HistoryViewModel::SetCalculatorDisplay(CalculatorDisplay& calculatorDisplay void HistoryViewModel::ShowItem(_In_ HistoryItemViewModel ^ e) { + unsigned int index; + Items->IndexOf(e, &index); + TraceLogger::GetInstance().LogHistoryItemLoad((ViewMode)m_currentMode, ItemSize, (int)(index)); HistoryItemClicked(e); } @@ -149,7 +152,6 @@ void HistoryViewModel::OnHideCommand(_In_ Platform::Object ^ e) void HistoryViewModel::OnClearCommand(_In_ Platform::Object ^ e) { - TraceLogger::GetInstance().LogClearHistory(); if (AreHistoryShortcutsEnabled == true) { m_calculatorManager->ClearHistory(); diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index dd0283da6..af2c7bc41 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -35,8 +35,10 @@ namespace StringReference IsStandardPropertyName(L"IsStandard"); StringReference IsScientificPropertyName(L"IsScientific"); StringReference IsProgrammerPropertyName(L"IsProgrammer"); + StringReference IsAlwaysOnTopPropertyName(L"IsAlwaysOnTop"); StringReference DisplayValuePropertyName(L"DisplayValue"); StringReference CalculationResultAutomationNamePropertyName(L"CalculationResultAutomationName"); + StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked"); } namespace CalculatorResourceKeys @@ -65,6 +67,7 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() , m_HexDisplayValue(L"0") , m_BinaryDisplayValue(L"0") , m_OctalDisplayValue(L"0") + , m_BinaryDigits(ref new Vector(64, false)) , m_standardCalculatorManager(&m_calculatorDisplay, &m_resourceProvider) , m_ExpressionTokens(ref new Vector()) , m_MemorizedNumbers(ref new Vector()) @@ -72,10 +75,7 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() , m_IsFToEChecked(false) , m_isShiftChecked(false) , m_IsShiftProgrammerChecked(false) - , m_IsQwordEnabled(true) - , m_IsDwordEnabled(true) - , m_IsWordEnabled(true) - , m_IsByteEnabled(true) + , m_valueBitLength(BitLength::BitLengthQWord) , m_isBitFlipChecked(false) , m_isBinaryBitFlippingEnabled(false) , m_CurrentRadixType(RADIX_TYPE::DEC_RADIX) @@ -201,7 +201,12 @@ void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ wstring const& displayS // not match what the narrator is saying m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue, localizedDisplayStringValue, isError); - DisplayValue = localizedDisplayStringValue; + AreAlwaysOnTopResultsUpdated = false; + if (DisplayValue != localizedDisplayStringValue) + { + DisplayValue = localizedDisplayStringValue; + AreAlwaysOnTopResultsUpdated = true; + } IsInError = isError; @@ -414,7 +419,7 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector& new memorySlot->Value = Utils::LRO + ref new String(stringValue.c_str()) + Utils::PDF; MemorizedNumbers->InsertAt(0, memorySlot); - IsMemoryEmpty = false; + IsMemoryEmpty = IsAlwaysOnTop; // Update the slot position for the rest of the slots for (unsigned int i = 1; i < MemorizedNumbers->Size; i++) @@ -599,8 +604,6 @@ void StandardCalculatorViewModel::OnButtonPressed(Object ^ parameter) NumbersAndOperatorsEnum numOpEnum = CalculatorButtonPressedEventArgs::GetOperationFromCommandParameter(parameter); Command cmdenum = ConvertToOperatorsEnum(numOpEnum); - TraceLogger::GetInstance().UpdateFunctionUsage((int)numOpEnum); - if (IsInError) { m_standardCalculatorManager.SendCommand(Command::CommandCLEAR); @@ -668,31 +671,12 @@ void StandardCalculatorViewModel::OnButtonPressed(Object ^ parameter) m_isLastOperationHistoryLoad = false; } + TraceLogger::GetInstance().UpdateButtonUsage(numOpEnum, GetCalculatorMode()); m_standardCalculatorManager.SendCommand(cmdenum); } } } -int StandardCalculatorViewModel::GetBitLengthType() -{ - if (IsQwordEnabled) - { - return QwordType; - } - else if (IsDwordEnabled) - { - return DwordType; - } - else if (IsWordEnabled) - { - return WordType; - } - else - { - return ByteType; - } -} - int StandardCalculatorViewModel::GetNumberBase() { if (CurrentRadixType == HEX_RADIX) @@ -723,9 +707,10 @@ void StandardCalculatorViewModel::OnCopyCommand(Object ^ parameter) void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter) { + auto that(this); ViewMode mode; int NumberBase = -1; - int bitLengthType = -1; + BitLength bitLengthType = BitLength::BitLengthUnknown; if (IsScientific) { mode = ViewMode::Scientific; @@ -734,7 +719,7 @@ void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter) { mode = ViewMode::Programmer; NumberBase = GetNumberBase(); - bitLengthType = GetBitLengthType(); + bitLengthType = m_valueBitLength; } else { @@ -748,7 +733,7 @@ void StandardCalculatorViewModel::OnPasteCommand(Object ^ parameter) // Ensure that the paste happens on the UI thread CopyPasteManager::GetStringToPaste(mode, NavCategory::GetGroupType(mode), NumberBase, bitLengthType) - .then([this, mode](String ^ pastedString) { OnPaste(pastedString, mode); }, concurrency::task_continuation_context::use_current()); + .then([that, mode](String ^ pastedString) { that->OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum(NumbersAndOperatorsEnum operation) @@ -756,7 +741,7 @@ CalculationManager::Command StandardCalculatorViewModel::ConvertToOperatorsEnum( return safe_cast(operation); } -void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) +void StandardCalculatorViewModel::OnPaste(String ^ pastedString) { // If pastedString is invalid("NoOp") then display pasteError else process the string if (pastedString == StringReference(CopyPasteManager::PasteErrorString)) @@ -765,7 +750,7 @@ void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) return; } - TraceLogger::GetInstance().LogValidInputPasted(mode); + TraceLogger::GetInstance().LogInputPasted(GetCalculatorMode()); bool isFirstLegalChar = true; m_standardCalculatorManager.SendCommand(Command::CommandCENTR); bool sendNegate = false; @@ -884,7 +869,7 @@ void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) // Handle exponent and exponent sign (...e+... or ...e-... or ...e...) if (mappedNumOp == NumbersAndOperatorsEnum::Exp) { - //Check the following item + // Check the following item switch (MapCharacterToButtonId(*(it + 1), canSendNegate)) { case NumbersAndOperatorsEnum::Subtract: @@ -896,7 +881,7 @@ void StandardCalculatorViewModel::OnPaste(String ^ pastedString, ViewMode mode) break; case NumbersAndOperatorsEnum::Add: { - //Nothing to do, skip to the next item + // Nothing to do, skip to the next item ++it; } break; @@ -911,8 +896,7 @@ void StandardCalculatorViewModel::OnClearMemoryCommand(Object ^ parameter) { m_standardCalculatorManager.MemorizedNumberClearAll(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().LogMemoryClearAll(windowId); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode()); String ^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement(CalculatorResourceKeys::MemoryCleared, m_localizedMemoryCleared); Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(announcement); @@ -1046,8 +1030,7 @@ void StandardCalculatorViewModel::OnMemoryButtonPressed() { m_standardCalculatorManager.MemorizeNumber(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::Memory, GetCalculatorMode()); String ^ announcement = LocalizationStringUtil::GetLocalizedNarratorAnnouncement( CalculatorResourceKeys::MemorySave, m_localizedMemorySavedAutomationFormat, m_DisplayValue->Data()); @@ -1079,49 +1062,31 @@ void StandardCalculatorViewModel::OnMemoryItemPressed(Object ^ memoryItemPositio auto boxedPosition = safe_cast ^>(memoryItemPosition); m_standardCalculatorManager.MemorizedNumberLoad(boxedPosition->Value); HideMemoryClicked(); - int windowId = Utils::GetWindowId(); - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); + + auto mode = IsStandard ? ViewMode::Standard : IsScientific ? ViewMode::Scientific : ViewMode::Programmer; + TraceLogger::GetInstance().LogMemoryItemLoad(mode, MemorizedNumbers->Size, boxedPosition->Value); } } void StandardCalculatorViewModel::OnMemoryAdd(Object ^ memoryItemPosition) { // M+ will add display to memorylist if memory list is empty. - int windowId = Utils::GetWindowId(); if (MemorizedNumbers) { auto boxedPosition = safe_cast ^>(memoryItemPosition); - if (MemorizedNumbers->Size > 0) - { - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer); - } - else - { - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); - } + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryAdd, GetCalculatorMode()); m_standardCalculatorManager.MemorizedNumberAdd(boxedPosition->Value); } } void StandardCalculatorViewModel::OnMemorySubtract(Object ^ memoryItemPosition) { - int windowId = Utils::GetWindowId(); - // M- will add negative of displayed number to memorylist if memory list is empty. if (MemorizedNumbers) { auto boxedPosition = safe_cast ^>(memoryItemPosition); - if (MemorizedNumbers->Size > 0) - { - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().UpdateMemoryMap(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer); - } - else - { - TraceLogger::GetInstance().InsertIntoMemoryMap(windowId, IsStandard, IsScientific, IsProgrammer); - } + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::MemorySubtract, GetCalculatorMode()); m_standardCalculatorManager.MemorizedNumberSubtract(boxedPosition->Value); } } @@ -1130,7 +1095,6 @@ void StandardCalculatorViewModel::OnMemoryClear(_In_ Object ^ memoryItemPosition { if (MemorizedNumbers && MemorizedNumbers->Size > 0) { - int windowId = Utils::GetWindowId(); auto boxedPosition = safe_cast ^>(memoryItemPosition); if (boxedPosition->Value >= 0) @@ -1148,9 +1112,7 @@ void StandardCalculatorViewModel::OnMemoryClear(_In_ Object ^ memoryItemPosition { IsMemoryEmpty = true; } - - TraceLogger::GetInstance().LogMemoryUsed(windowId, boxedPosition->Value, IsStandard, IsScientific, IsProgrammer, MemorizedNumbers->Size); - TraceLogger::GetInstance().DeleteFromMemoryMap(windowId, boxedPosition->Value); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::MemoryClear, GetCalculatorMode()); wstring localizedIndex = to_wstring(boxedPosition->Value + 1); LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex); @@ -1191,6 +1153,11 @@ void StandardCalculatorViewModel::OnPropertyChanged(String ^ propertyname) RaisePropertyChanged(CalculationResultAutomationNamePropertyName); Announcement = GetDisplayUpdatedNarratorAnnouncement(); } + else if (propertyname == IsBitFlipCheckedPropertyName) + { + TraceLogger::GetInstance().UpdateButtonUsage( + IsBitFlipChecked ? NumbersAndOperatorsEnum::BitflipButton : NumbersAndOperatorsEnum::FullKeypadButton, ViewMode::Programmer); + } } void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState) @@ -1223,7 +1190,7 @@ void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState) } } -String^ StandardCalculatorViewModel::GetRawDisplayValue() +String ^ StandardCalculatorViewModel::GetRawDisplayValue() { if (IsInError) { @@ -1654,6 +1621,7 @@ wstring StandardCalculatorViewModel::AddPadding(wstring binaryString) void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() { + constexpr int32_t precision = 64; wstring hexDisplayString; wstring decimalDisplayString; wstring octalDisplayString; @@ -1661,8 +1629,7 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() if (!IsInError) { // we want the precision to be set to maximum value so that the autoconversions result as desired - int32_t precision = 64; - if (m_standardCalculatorManager.GetResultForRadix(16, precision) == L"") + if ((hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision, true)) == L"") { hexDisplayString = DisplayValue->Data(); decimalDisplayString = DisplayValue->Data(); @@ -1671,10 +1638,9 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() } else { - hexDisplayString = m_standardCalculatorManager.GetResultForRadix(16, precision); - decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision); - octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision); - binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision); + decimalDisplayString = m_standardCalculatorManager.GetResultForRadix(10, precision, true); + octalDisplayString = m_standardCalculatorManager.GetResultForRadix(8, precision, true); + binaryDisplayString = m_standardCalculatorManager.GetResultForRadix(2, precision, true); } } const auto& localizer = LocalizationSettings::GetInstance(); @@ -1693,6 +1659,17 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() DecDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedDecimalAutomationFormat, DecimalDisplayValue); OctDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedOctalAutomationFormat, GetNarratorStringReadRawNumbers(OctalDisplayValue)); BinDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedBinaryAutomationFormat, GetNarratorStringReadRawNumbers(BinaryDisplayValue)); + + auto binaryValueArray = ref new Vector(64, false); + auto binaryValue = m_standardCalculatorManager.GetResultForRadix(2, precision, false); + int i = 0; + + // To get bit 0, grab from opposite end of string. + for (std::wstring::reverse_iterator it = binaryValue.rbegin(); it != binaryValue.rend(); ++it) + { + binaryValueArray->SetAt(i++, *it == L'1'); + } + BinaryDigits = binaryValueArray; } void StandardCalculatorViewModel::SwitchAngleType(NumbersAndOperatorsEnum num) @@ -1884,3 +1861,44 @@ NarratorAnnouncement ^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnn return CalculatorAnnouncement::GetDisplayUpdatedAnnouncement(announcement); } + +ViewMode StandardCalculatorViewModel::GetCalculatorMode() +{ + if (IsStandard) + { + return ViewMode::Standard; + } + else if (IsScientific) + { + return ViewMode::Scientific; + } + return ViewMode::Programmer; +} + +void StandardCalculatorViewModel::ValueBitLength::set(CalculatorApp::Common::BitLength value) +{ + if (m_valueBitLength != value) + { + m_valueBitLength = value; + RaisePropertyChanged(L"ValueBitLength"); + + switch (value) + { + case BitLength::BitLengthQWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Qword); + break; + case BitLength::BitLengthDWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Dword); + break; + case BitLength::BitLengthWord: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Word); + break; + case BitLength::BitLengthByte: + ButtonPressed->Execute(NumbersAndOperatorsEnum::Byte); + break; + } + + // update memory list according to bit length + SetMemorizedNumbersString(); + } +} diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index 59dbbcb16..77480dd80 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -9,6 +9,7 @@ #include "Common/CalculatorButtonUser.h" #include "HistoryViewModel.h" #include "MemoryItemViewModel.h" +#include "Common/BitLength.h" namespace CalculatorFunctionalTests { @@ -40,7 +41,6 @@ namespace CalculatorApp StandardCalculatorViewModel(); void UpdateOperand(int pos, Platform::String ^ text); void UpdatecommandsInRecordingMode(); - int GetBitLengthType(); int GetNumberBase(); OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); @@ -54,6 +54,7 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_RW(Platform::String ^, HexDisplayValue); OBSERVABLE_PROPERTY_RW(Platform::String ^, OctalDisplayValue); OBSERVABLE_NAMED_PROPERTY_RW(Platform::String ^, BinaryDisplayValue); + OBSERVABLE_NAMED_PROPERTY_R(Windows::Foundation::Collections::IVector ^, BinaryDigits); OBSERVABLE_PROPERTY_RW(Platform::String ^, HexDisplayValue_AutomationName); OBSERVABLE_PROPERTY_RW(Platform::String ^, DecDisplayValue_AutomationName); OBSERVABLE_PROPERTY_RW(Platform::String ^, OctDisplayValue_AutomationName); @@ -72,12 +73,9 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_RW(Platform::String ^, CalculationResultAutomationName); OBSERVABLE_PROPERTY_RW(Platform::String ^, CalculationExpressionAutomationName); OBSERVABLE_PROPERTY_RW(bool, IsShiftProgrammerChecked); - OBSERVABLE_PROPERTY_RW(bool, IsQwordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsDwordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsWordEnabled); - OBSERVABLE_PROPERTY_RW(bool, IsByteEnabled); OBSERVABLE_PROPERTY_RW(int, CurrentRadixType); OBSERVABLE_PROPERTY_RW(bool, AreTokensUpdated); + OBSERVABLE_PROPERTY_RW(bool, AreAlwaysOnTopResultsUpdated); OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); OBSERVABLE_PROPERTY_RW(bool, AreProgrammerRadixOperatorsEnabled); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement ^, Announcement); @@ -127,6 +125,13 @@ namespace CalculatorApp } } } + static property Platform::String ^ IsBitFlipCheckedPropertyName + { + Platform::String ^ get() + { + return Platform::StringReference(L"IsBitFlipChecked"); + } + } property bool IsBinaryBitFlippingEnabled { @@ -144,6 +149,15 @@ namespace CalculatorApp } } + property CalculatorApp::Common::BitLength ValueBitLength + { + CalculatorApp::Common::BitLength get() + { + return m_valueBitLength; + } + void set(CalculatorApp::Common::BitLength value); + } + property bool IsStandard { bool get() @@ -212,6 +226,29 @@ namespace CalculatorApp } } } + static property Platform::String ^ IsProgrammerPropertyName + { + Platform::String ^ get() + { + return Platform::StringReference(L"IsProgrammer"); + } + } + + property bool IsAlwaysOnTop + { + bool get() + { + return m_isAlwaysOnTop; + } + void set(bool value) + { + if (m_isAlwaysOnTop != value) + { + m_isAlwaysOnTop = value; + RaisePropertyChanged(L"IsAlwaysOnTop"); + } + } + } property bool IsEditingEnabled { @@ -317,7 +354,7 @@ namespace CalculatorApp } } - internal : void OnPaste(Platform::String ^ pastedString, CalculatorApp::Common::ViewMode mode); + internal : void OnPaste(Platform::String ^ pastedString); void OnCopyCommand(Platform::Object ^ parameter); void OnPasteCommand(Platform::Object ^ parameter); @@ -348,7 +385,6 @@ namespace CalculatorApp void OnBinaryOperatorReceived(); void OnMemoryItemChanged(unsigned int indexOfMemory); - Platform::String ^ GetLocalizedStringFormat(Platform::String ^ format, Platform::String ^ displayValue); void OnPropertyChanged(Platform::String ^ propertyname); void SetCalculatorType(CalculatorApp::Common::ViewMode targetState); @@ -407,6 +443,7 @@ namespace CalculatorApp bool m_isStandard; bool m_isScientific; bool m_isProgrammer; + bool m_isAlwaysOnTop; bool m_isBinaryBitFlippingEnabled; bool m_isBitFlipChecked; bool m_isShiftChecked; @@ -416,6 +453,7 @@ namespace CalculatorApp bool m_operandUpdated; bool m_completeTextSelection; bool m_isLastOperationHistoryLoad; + CalculatorApp::Common::BitLength m_valueBitLength; Platform::String ^ m_selectedExpressionLastData; Common::DisplayExpressionToken ^ m_selectedExpressionToken; @@ -451,6 +489,8 @@ namespace CalculatorApp bool IsViewPinned(); void SetViewPinnedState(bool pinned); + CalculatorApp::Common::ViewMode GetCalculatorMode(); + friend class CalculatorDisplay; friend class CalculatorFunctionalTests::HistoryTests; friend class CalculatorUnitTests::MultiWindowUnitTests; diff --git a/src/CalcViewModel/UnitConverterViewModel.cpp b/src/CalcViewModel/UnitConverterViewModel.cpp index 821cdc707..e57b0dc3a 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -148,7 +148,6 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptrInitialize(); PopulateData(); } @@ -156,7 +155,6 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptrSendCommand(UCM::Command::Reset); - m_IsFirstTime = true; OnCategoryChanged(nullptr); } @@ -239,15 +237,8 @@ void UnitConverterViewModel::OnUnitChanged(Object ^ parameter) // End timer to show results immediately m_supplementaryResultsTimer->Cancel(); } - if (!m_IsFirstTime) - { - SaveUserPreferences(); - } - else - { - RestoreUserPreferences(); - m_IsFirstTime = false; - } + + SaveUserPreferences(); } void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused) @@ -502,6 +493,8 @@ void UnitConverterViewModel::OnButtonPressed(Platform::Object ^ parameter) } m_model->SendCommand(command); + + TraceLogger::GetInstance().LogConverterInputReceived(Mode); } void UnitConverterViewModel::OnCopyCommand(Platform::Object ^ parameter) @@ -523,7 +516,7 @@ void UnitConverterViewModel::OnPasteCommand(Platform::Object ^ parameter) // EventWriteClipboardPaste_Start(); // Any converter ViewMode is fine here. CopyPasteManager::GetStringToPaste(m_Mode, NavCategory::GetGroupType(m_Mode)) - .then([this](String ^ pastedString) { OnPaste(pastedString, m_Mode); }, concurrency::task_continuation_context::use_current()); + .then([this](String ^ pastedString) { OnPaste(pastedString); }, concurrency::task_continuation_context::use_current()); } void UnitConverterViewModel::InitializeView() @@ -882,7 +875,7 @@ NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId(const wch return mappedValue; } -void UnitConverterViewModel::OnPaste(String ^ stringToPaste, ViewMode mode) +void UnitConverterViewModel::OnPaste(String ^ stringToPaste) { // If pastedString is invalid("NoOp") then display pasteError else process the string if (stringToPaste == StringReference(CopyPasteManager::PasteErrorString)) @@ -891,7 +884,7 @@ void UnitConverterViewModel::OnPaste(String ^ stringToPaste, ViewMode mode) return; } - TraceLogger::GetInstance().LogValidInputPasted(mode); + TraceLogger::GetInstance().LogInputPasted(Mode); bool isFirstLegalChar = true; bool sendNegate = false; wstring accumulation = L""; @@ -1014,7 +1007,6 @@ void UnitConverterViewModel::StartConversionResultTimer() { String ^ valueFrom = m_Value1Active ? m_Value1 : m_Value2; String ^ valueTo = m_Value1Active ? m_Value2 : m_Value1; - TraceLogger::GetInstance().LogConversionResult(valueFrom->Data(), UnitFrom->ToString()->Data(), valueTo->Data(), UnitTo->ToString()->Data()); } }); } diff --git a/src/CalcViewModel/UnitConverterViewModel.h b/src/CalcViewModel/UnitConverterViewModel.h index b0cc02413..6a8761a32 100644 --- a/src/CalcViewModel/UnitConverterViewModel.h +++ b/src/CalcViewModel/UnitConverterViewModel.h @@ -204,7 +204,7 @@ namespace CalculatorApp NumbersAndOperatorsEnum MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate); void DisplayPasteError(); void OnValueActivated(IActivatable ^ control); - void OnPaste(Platform::String ^ stringToPaste, CalculatorApp::Common::ViewMode mode); + void OnPaste(Platform::String ^ stringToPaste); void OnCopyCommand(Platform::Object ^ parameter); void OnPasteCommand(Platform::Object ^ parameter); @@ -311,8 +311,6 @@ namespace CalculatorApp std::wstring m_valueFromUnlocalized; std::wstring m_valueToUnlocalized; bool m_relocalizeStringOnSwitch; - // For Saving the User Preferences only if the Unit converter ViewModel is initialised for the first time - bool m_IsFirstTime; Platform::String ^ m_localizedValueFromFormat; Platform::String ^ m_localizedValueFromDecimalFormat; diff --git a/src/CalcViewModel/pch.h b/src/CalcViewModel/pch.h index 142e05f85..575a1f0d6 100644 --- a/src/CalcViewModel/pch.h +++ b/src/CalcViewModel/pch.h @@ -30,6 +30,7 @@ #include #include #include +#include // C++\WinRT Headers #include "winrt/base.h" #include "winrt/Windows.Foundation.Diagnostics.h" diff --git a/src/Calculator/AboutFlyout.xaml b/src/Calculator/AboutFlyout.xaml index c2d82f47b..50f6f4e1e 100644 --- a/src/Calculator/AboutFlyout.xaml +++ b/src/Calculator/AboutFlyout.xaml @@ -4,7 +4,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:CalculatorApp.Common" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - Loaded="UserControl_Loaded" mc:Ignorable="d"> diff --git a/src/Calculator/AboutFlyout.xaml.cpp b/src/Calculator/AboutFlyout.xaml.cpp index 6ec7d4e16..87a15e693 100644 --- a/src/Calculator/AboutFlyout.xaml.cpp +++ b/src/Calculator/AboutFlyout.xaml.cpp @@ -61,8 +61,3 @@ void AboutFlyout::SetDefaultFocus() { AboutFlyoutEULA->Focus(::FocusState::Programmatic); } - -void CalculatorApp::AboutFlyout::UserControl_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) -{ - TraceLogger::GetInstance().LogAboutFlyoutOpened(); -} diff --git a/src/Calculator/AboutFlyout.xaml.h b/src/Calculator/AboutFlyout.xaml.h index 87b53e9c8..ab0e481ef 100644 --- a/src/Calculator/AboutFlyout.xaml.h +++ b/src/Calculator/AboutFlyout.xaml.h @@ -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 @@ -18,6 +18,5 @@ public private: void FeedbackButton_Click(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e); void SetVersionString(); - void UserControl_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); }; } /* namespace CalculatorApp */ diff --git a/src/Calculator/App.xaml b/src/Calculator/App.xaml index 14fb102fc..cff324d8e 100644 --- a/src/Calculator/App.xaml +++ b/src/Calculator/App.xaml @@ -134,6 +134,8 @@ 24 20 + 15 + 12 15 @@ -216,11 +218,21 @@ TargetType="Controls:CalculatorButton"> + + + + + + + + @@ -237,7 +348,6 @@ - @@ -289,6 +399,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -319,7 +451,7 @@ - + @@ -365,7 +497,7 @@ - + @@ -378,6 +510,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -420,24 +576,34 @@ + MinHeight="0"/> + MinHeight="20"/> + MinHeight="0"/> + - + + AutomationProperties.AutomationId="CalculatorAlwaysOnTopResults" + AutomationProperties.HeadingLevel="Level1" + AutomationProperties.Name="{x:Bind Model.CalculationResultAutomationName, Mode=OneWay}" + TokensUpdated="{x:Bind Model.AreAlwaysOnTopResultsUpdated, Mode=OneWay}" + HorizontalContentAlignment="Right" + IsActive="True" + UseSystemFocusVisuals="True" + Visibility="{x:Bind Model.IsAlwaysOnTop, Mode=OneWay}"/> - - + + + + + + @@ -583,7 +759,7 @@ x:Uid="MemoryFlyout" AutomationProperties.AutomationId="MemoryFlyout" Closed="OnMemoryFlyoutClosed" - FlyoutPresenterStyle="{ThemeResource MemoryFlyoutStyle}" + FlyoutPresenterStyle="{StaticResource MemoryFlyoutStyle}" Opened="OnMemoryFlyoutOpened" Placement="Full"/> @@ -944,7 +1120,6 @@ diff --git a/src/Calculator/Views/Calculator.xaml.cpp b/src/Calculator/Views/Calculator.xaml.cpp index 3a64988fc..ce84bb71d 100644 --- a/src/Calculator/Views/Calculator.xaml.cpp +++ b/src/Calculator/Views/Calculator.xaml.cpp @@ -40,6 +40,7 @@ using namespace Windows::UI::ViewManagement; DEPENDENCY_PROPERTY_INITIALIZATION(Calculator, IsStandard); DEPENDENCY_PROPERTY_INITIALIZATION(Calculator, IsScientific); DEPENDENCY_PROPERTY_INITIALIZATION(Calculator, IsProgrammer); +DEPENDENCY_PROPERTY_INITIALIZATION(Calculator, IsAlwaysOnTop); Calculator::Calculator() : m_doAnimate(false) @@ -60,6 +61,8 @@ Calculator::Calculator() auto resLoader = AppResourceProvider::GetInstance(); CopyMenuItem->Text = resLoader.GetResourceString(L"copyMenuItem"); PasteMenuItem->Text = resLoader.GetResourceString(L"pasteMenuItem"); + + this->SizeChanged += ref new SizeChangedEventHandler(this, &Calculator::Calculator_SizeChanged); } void Calculator::LoadResourceStrings() @@ -97,7 +100,7 @@ void Calculator::SetFontSizeResources() { L"Tibt", 104, 29.333, 20, 40, 56, 40, 56 }, { L"Default", 104, 29.333, 23, 40, 56, 40, 56 } }; - DecimalFormatter^ formatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDecimalFormatter(); + DecimalFormatter ^ formatter = LocalizationService::GetInstance()->GetRegionalSettingsAwareDecimalFormatter(); const FontTable* currentItem = fontTables; while (currentItem->numericSystem.compare(std::wstring(L"Default")) != 0 && currentItem->numericSystem.compare(formatter->NumeralSystem->Data()) != 0) @@ -135,7 +138,7 @@ void Calculator::OnLoaded(_In_ Object ^, _In_ RoutedEventArgs ^) WeakReference weakThis(this); this->Dispatcher->RunAsync( CoreDispatcherPriority::Normal, ref new DispatchedHandler([weakThis]() { - if (TraceLogger::GetInstance().UpdateWindowIdLog(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()))) + if (TraceLogger::GetInstance().IsWindowIdInLog(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()))) { auto refThis = weakThis.Resolve(); if (refThis != nullptr) @@ -279,6 +282,35 @@ void Calculator::OnIsProgrammerPropertyChanged(bool /*oldValue*/, bool newValue) UpdatePanelViewState(); } +void Calculator::OnIsAlwaysOnTopPropertyChanged(bool /*oldValue*/, bool newValue) +{ + if (newValue) + { + VisualStateManager::GoToState(this, L"AlwaysOnTop", false); + } + else + { + VisualStateManager::GoToState(this, L"Normal", false); + if (Model->IsInError) + { + VisualStateManager::GoToState(this, L"ErrorLayout", false); + } + else + { + EnableMemoryControls(true); + } + } + + Model->IsMemoryEmpty = (Model->MemorizedNumbers->Size == 0) || IsAlwaysOnTop; + + AlwaysOnTopResults->UpdateScrollButtons(); + Results->UpdateTextState(); + + UpdateViewState(); + UpdatePanelViewState(); + SetDefaultFocus(); +} + void Calculator::OnIsInErrorPropertyChanged() { bool isError = Model->IsInError; @@ -395,34 +427,37 @@ void Calculator::UpdateHistoryState() void Calculator::UpdateMemoryState() { - if (!Model->IsMemoryEmpty) - { - MemRecall->IsEnabled = true; - ClearMemoryButton->IsEnabled = true; - } - else + if (!IsAlwaysOnTop) { - MemRecall->IsEnabled = false; - ClearMemoryButton->IsEnabled = false; - } + if (!Model->IsMemoryEmpty) + { + MemRecall->IsEnabled = true; + ClearMemoryButton->IsEnabled = true; + } + else + { + MemRecall->IsEnabled = false; + ClearMemoryButton->IsEnabled = false; + } - String ^ viewState = App::GetAppViewState(); - if (viewState == ViewState::DockedView) - { - CloseMemoryFlyout(); - SetChildAsMemory(); - MemoryButton->Visibility = ::Visibility::Collapsed; + String ^ viewState = App::GetAppViewState(); + if (viewState == ViewState::DockedView) + { + CloseMemoryFlyout(); + SetChildAsMemory(); + MemoryButton->Visibility = ::Visibility::Collapsed; - if (m_IsLastFlyoutMemory && !IsProgrammer) + if (m_IsLastFlyoutMemory && !IsProgrammer) + { + DockPivot->SelectedIndex = 1; + } + } + else { - DockPivot->SelectedIndex = 1; + MemoryButton->Visibility = ::Visibility::Visible; + DockMemoryHolder->Child = nullptr; } } - else - { - MemoryButton->Visibility = ::Visibility::Visible; - DockMemoryHolder->Child = nullptr; - } } void Calculator::SetChildAsMemory() @@ -450,13 +485,11 @@ void Calculator::OnHistoryItemClicked(_In_ HistoryItemViewModel ^ e) unsigned int tokenSize; assert(e->GetTokens() != nullptr); e->GetTokens()->GetSize(&tokenSize); - TraceLogger::GetInstance().LogHistoryItemLoadBegin(); Model->SetHistoryExpressionDisplay(e->GetTokens(), e->GetCommands()); Model->SetExpressionDisplay(e->GetTokens(), e->GetCommands()); Model->SetPrimaryDisplay(e->Result->Data(), false); Model->IsFToEEnabled = false; - TraceLogger::GetInstance().LogHistoryItemLoadEnd(tokenSize); CloseHistoryFlyout(); this->Focus(::FocusState::Programmatic); } @@ -468,8 +501,6 @@ void Calculator::HistoryFlyout_Opened(_In_ Object ^ sender, _In_ Object ^ args) m_IsLastFlyoutHistory = true; EnableControls(false); AutomationProperties::SetName(HistoryButton, m_closeHistoryFlyoutAutomationName); - TraceLogger::GetInstance().LogHistoryFlyoutOpenEnd(Model->HistoryVM->ItemSize); - TraceLogger::GetInstance().LogHistoryBodyOpened(); } void Calculator::HistoryFlyout_Closing(_In_ FlyoutBase ^ sender, _In_ FlyoutBaseClosingEventArgs ^ args) @@ -511,7 +542,14 @@ void Calculator::CloseMemoryFlyout() void Calculator::SetDefaultFocus() { - Results->Focus(::FocusState::Programmatic); + if (!IsAlwaysOnTop) + { + Results->Focus(::FocusState::Programmatic); + } + else + { + AlwaysOnTopResults->Focus(::FocusState::Programmatic); + } } void Calculator::ToggleHistoryFlyout(Object ^ /*parameter*/) @@ -526,7 +564,6 @@ void Calculator::ToggleHistoryFlyout(Object ^ /*parameter*/) } else { - TraceLogger::GetInstance().LogHistoryFlyoutOpenBegin(Model->HistoryVM->ItemSize); HistoryFlyout->Content = m_historyList; m_historyList->RowHeight = NumpadPanel->ActualHeight; FlyoutBase::ShowAttachedFlyout(HistoryButton); @@ -545,7 +582,6 @@ void Calculator::ToggleMemoryFlyout() } else { - TraceLogger::GetInstance().LogMemoryFlyoutOpenBegin(Model->MemorizedNumbers->Size); MemoryFlyout->Content = GetMemory(); m_memory->RowHeight = NumpadPanel->ActualHeight; FlyoutBase::ShowAttachedFlyout(MemoryButton); @@ -555,13 +591,11 @@ void Calculator::ToggleMemoryFlyout() void Calculator::OnMemoryFlyoutOpened(_In_ Object ^ sender, _In_ Object ^ args) { - TraceLogger::GetInstance().LogMemoryFlyoutOpenEnd(Model->MemorizedNumbers->Size); m_IsLastFlyoutMemory = true; m_IsLastFlyoutHistory = false; m_fIsMemoryFlyoutOpen = true; AutomationProperties::SetName(MemoryButton, m_closeMemoryFlyoutAutomationName); EnableControls(false); - TraceLogger::GetInstance().LogMemoryBodyOpened(); } void Calculator::OnMemoryFlyoutClosing(_In_ FlyoutBase ^ sender, _In_ FlyoutBaseClosingEventArgs ^ args) @@ -700,14 +734,17 @@ void Calculator::OnMemoryAccessKeyInvoked(_In_ UIElement ^ sender, _In_ AccessKe DockPivot->SelectedItem = MemoryPivotItem; } -void CalculatorApp::Calculator::DockPivot_SelectionChanged(Platform::Object ^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^ e) +void CalculatorApp::Calculator::OnVisualStateChanged(Platform::Object ^ sender, Windows::UI::Xaml::VisualStateChangedEventArgs ^ e) { - if (DockPivot->SelectedIndex == 0) - { - TraceLogger::GetInstance().LogHistoryBodyOpened(); - } - else + auto mode = IsStandard ? ViewMode::Standard : IsScientific ? ViewMode::Scientific : ViewMode::Programmer; + auto state = std::wstring(e->NewState->Name->Begin()); + TraceLogger::GetInstance().LogVisualStateChanged(mode, state, IsAlwaysOnTop); +} + +void Calculator::Calculator_SizeChanged(Object ^ /*sender*/, SizeChangedEventArgs ^ /*e*/) +{ + if (Model->IsAlwaysOnTop) { - TraceLogger::GetInstance().LogMemoryBodyOpened(); + AlwaysOnTopResults->UpdateScrollButtons(); } } diff --git a/src/Calculator/Views/Calculator.xaml.h b/src/Calculator/Views/Calculator.xaml.h index 2ea2e81c1..f91ba6c85 100644 --- a/src/Calculator/Views/Calculator.xaml.h +++ b/src/Calculator/Views/Calculator.xaml.h @@ -54,6 +54,7 @@ public DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, IsStandard, false); DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, IsScientific, false); DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, IsProgrammer, false); + DEPENDENCY_PROPERTY_WITH_DEFAULT_AND_CALLBACK(bool, IsAlwaysOnTop, false); COMMAND_FOR_METHOD(HistoryButtonPressed, Calculator::ToggleHistoryFlyout); @@ -76,12 +77,12 @@ public void UpdateMemoryState(); void UpdateHistoryState(); - void CalculationResultsOnSelected(_In_ Platform::Object ^ sender); void OnContextRequested(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::Input::ContextRequestedEventArgs ^ e); void OnContextCanceled(Windows::UI::Xaml::UIElement ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); void OnIsScientificPropertyChanged(bool oldValue, bool newValue); void OnIsProgrammerPropertyChanged(bool oldValue, bool newValue); void OnIsStandardPropertyChanged(bool oldValue, bool newValue); + void OnIsAlwaysOnTopPropertyChanged(bool oldValue, bool newValue); void OnIsInErrorPropertyChanged(); void OnCalcPropertyChanged(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::Data::PropertyChangedEventArgs ^ e); void OnStoryboardCompleted(_In_ Platform::Object ^ sender, _In_ Platform::Object ^ e); @@ -90,6 +91,7 @@ public void EnsureProgrammer(); void SetFontSizeResources(); std::wstring GetCurrentLayoutState(); + void Calculator_SizeChanged(Object ^ sender, Windows::UI::Xaml::SizeChangedEventArgs ^ e); private: Windows::UI::Xaml::Controls::ListView ^ m_tokenList; @@ -139,6 +141,6 @@ public void OnErrorLayoutCompleted(_In_ Platform::Object ^ sender, _In_ Platform::Object ^ e); void OnHistoryAccessKeyInvoked(_In_ Windows::UI::Xaml::UIElement ^ sender, _In_ Windows::UI::Xaml::Input::AccessKeyInvokedEventArgs ^ args); void OnMemoryAccessKeyInvoked(_In_ Windows::UI::Xaml::UIElement ^ sender, _In_ Windows::UI::Xaml::Input::AccessKeyInvokedEventArgs ^ args); - void DockPivot_SelectionChanged(Platform::Object ^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs ^ e); + void OnVisualStateChanged(Platform::Object ^ sender, Windows::UI::Xaml::VisualStateChangedEventArgs ^ e); }; } diff --git a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml index f5de3ad21..e5f8193cd 100644 --- a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml +++ b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:common="using:CalculatorApp.Common" xmlns:controls="using:CalculatorApp.Controls" - xmlns:converters="using:CalculatorApp.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:CalculatorApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -14,11 +13,6 @@ Unloaded="OnUnloaded" mc:Ignorable="d"> - - - - - - + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 63), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 63 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 62), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 62 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 61), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 61 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 60), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 60 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 59), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 59 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 58), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 58 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 57), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 57 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 56), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 56 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 55), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 55 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 54), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 54 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 53), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 53 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 52), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 52 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 51), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 51 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 50), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 50 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 49), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 49 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 48), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 48 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 47), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 47 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 46), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 46 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 45), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 45 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 44), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 44 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 43), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 43 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 42), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 42 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 41), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 41 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 40), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 40 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 39), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 39 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 38), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 38 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 37), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 37 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 36), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 36 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 35), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 35 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 34), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 34 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 33), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 33 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 32), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 32 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 31), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 31 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 30), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 30 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 29), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 29 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 28), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 28 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 27), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 27 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 26), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 26 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 25), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 25 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 24), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 24 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 23), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 23 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 22), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 22 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 21), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 21 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 20), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 20 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 19), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 19 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 18), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 18 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 17), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 17 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 16), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 16 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 15), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 15 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 14), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 14 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 13), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 13 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 12), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 12 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 11), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 11 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 10), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 10 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 9), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 9 + + + IsEnabled="{x:Bind ShouldEnableBit(Model.ValueBitLength, 8), Mode=OneWay}" + Unchecked="OnBitToggled"> + + 8 + + + Unchecked="OnBitToggled"> + + 7 + + + Unchecked="OnBitToggled"> + + 6 + + + Unchecked="OnBitToggled"> + + 5 + + + Unchecked="OnBitToggled"> + + 4 + + + Unchecked="OnBitToggled"> + + 3 + + + Unchecked="OnBitToggled"> + + 2 + + + Unchecked="OnBitToggled"> + + 1 + + + Unchecked="OnBitToggled"> + + 0 + + diff --git a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.cpp b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.cpp index 418ff3364..068f48d69 100644 --- a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.cpp +++ b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.cpp @@ -11,6 +11,8 @@ #include "CalcViewModel/Common/TraceLogger.h" #include "CalcViewModel/Common/LocalizationSettings.h" #include "Converters/BooleanToVisibilityConverter.h" +#include +#include "CalcViewModel/Common/LocalizationStringUtil.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; @@ -19,6 +21,7 @@ using namespace CalculatorApp::ViewModel; using namespace Platform; using namespace std; using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Automation; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; @@ -29,15 +32,11 @@ CalculatorProgrammerBitFlipPanel::CalculatorProgrammerBitFlipPanel() : m_updatingCheckedStates(false) { InitializeComponent(); - auto booleanToVisibilityConverter = ref new Converters::BooleanToVisibilityConverter; - SetVisibilityBinding(BitFlipPanel, L"IsBinaryBitFlippingEnabled", booleanToVisibilityConverter); - AssignFlipButtons(); } void CalculatorProgrammerBitFlipPanel::OnLoaded(Object ^ sender, RoutedEventArgs ^ e) { - UnsubscribePropertyChanged(); SubscribePropertyChanged(); } @@ -51,8 +50,7 @@ void CalculatorProgrammerBitFlipPanel::SubscribePropertyChanged() if (Model != nullptr) { m_propertyChangedToken = Model->PropertyChanged += ref new PropertyChangedEventHandler(this, &CalculatorProgrammerBitFlipPanel::OnPropertyChanged); - - UpdateCheckedStates(); + UpdateCheckedStates(true); } } @@ -67,9 +65,19 @@ void CalculatorProgrammerBitFlipPanel::UnsubscribePropertyChanged() void CalculatorProgrammerBitFlipPanel::OnPropertyChanged(Object ^ sender, PropertyChangedEventArgs ^ e) { - if (e->PropertyName == StandardCalculatorViewModel::BinaryDisplayValuePropertyName) + if (e->PropertyName == StandardCalculatorViewModel::BinaryDigitsPropertyName) { - UpdateCheckedStates(); + UpdateCheckedStates(false); + } + else if (e->PropertyName == StandardCalculatorViewModel::IsBitFlipCheckedPropertyName + || e->PropertyName == StandardCalculatorViewModel::IsProgrammerPropertyName) + { + if (Model->IsBitFlipChecked && Model->IsProgrammer) + { + // OnBitToggle won't update the automation properties when this control isn't displayed + // We need to update all automation properties names manually when the BitFlipPanel is displayed again + UpdateAutomationPropertiesNames(); + } } } @@ -148,14 +156,6 @@ void CalculatorProgrammerBitFlipPanel::AssignFlipButtons() m_flipButtons[63] = this->Bit63; } -void CalculatorProgrammerBitFlipPanel::SetVisibilityBinding(_In_ FrameworkElement ^ element, _In_ String ^ path, _In_ IValueConverter ^ converter) -{ - Binding ^ commandBinding = ref new Binding(); - commandBinding->Path = ref new PropertyPath(path); - commandBinding->Converter = converter; - element->SetBinding(VisibilityProperty, commandBinding); -} - void CalculatorProgrammerBitFlipPanel::OnBitToggled(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { if (m_updatingCheckedStates) @@ -168,16 +168,16 @@ void CalculatorProgrammerBitFlipPanel::OnBitToggled(_In_ Object ^ sender, _In_ R // Also, if the mode is switched to other Calculator modes when the BitFlip panel is open, // a race condition exists in which the IsProgrammerMode property is still true and the UpdatePrimaryResult() is called, // which continuously alters the Display Value and the state of the Bit Flip buttons. - if ((Model->IsBitFlipChecked) && Model->IsProgrammer) + if (Model->IsBitFlipChecked && Model->IsProgrammer) { - TraceLogger::GetInstance().LogBitFlipUsed(); - auto flipButton = static_cast(sender); + int index = static_cast(flipButton->Tag); + flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, flipButton->IsChecked->Value)); Model->ButtonPressed->Execute(flipButton->ButtonId); } } -void CalculatorProgrammerBitFlipPanel::UpdateCheckedStates() +void CalculatorProgrammerBitFlipPanel::UpdateCheckedStates(bool updateAutomationPropertiesNames) { assert(!m_updatingCheckedStates); assert(m_flipButtons.size() == s_numBits); @@ -187,35 +187,57 @@ void CalculatorProgrammerBitFlipPanel::UpdateCheckedStates() return; } - static const wchar_t ch0 = LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit(L'0'); - - // Filter any unwanted characters from the displayed string. - static constexpr array unwantedChars = { L' ', Utils::LRE, Utils::PDF, Utils::LRO }; - - wstringstream stream; - wstring displayValue = Model->BinaryDisplayValue->Data(); - for (const wchar_t& c : displayValue) + m_updatingCheckedStates = true; + auto it = m_flipButtons.begin(); + int index = 0; + for (bool val : Model->BinaryDigits) { - if (find(begin(unwantedChars), end(unwantedChars), c) == unwantedChars.end()) + FlipButtons ^ flipButton = *it; + if (updateAutomationPropertiesNames) { - stream << c; + flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, flipButton->IsChecked->Value)); } + flipButton->IsChecked = val; + ++it; + ++index; } - wstring rawDisplay = stream.str(); - size_t paddingCount = s_numBits - rawDisplay.length(); - wstring setBits = wstring(paddingCount, ch0) + rawDisplay; - assert(setBits.length() == s_numBits); + m_updatingCheckedStates = false; +} - m_updatingCheckedStates = true; - for (unsigned int bitIndex = 0; bitIndex < s_numBits; bitIndex++) +void CalculatorProgrammerBitFlipPanel::UpdateAutomationPropertiesNames() +{ + for (FlipButtons ^ flipButton : m_flipButtons) { - // Highest bit (64) is at index 0 in bit string. - // To get bit 0, grab from opposite end of string. - wchar_t bit = setBits[s_numBits - bitIndex - 1]; + int index = static_cast(flipButton->Tag); + flipButton->SetValue(AutomationProperties::NameProperty, GenerateAutomationPropertiesName(index, flipButton->IsChecked->Value)); + } +} - m_flipButtons[bitIndex]->IsChecked = (bit != ch0); +bool CalculatorProgrammerBitFlipPanel::ShouldEnableBit(BitLength length, int index) +{ + switch (length) + { + case BitLength::BitLengthQWord: + return index <= 63; + case BitLength::BitLengthDWord: + return index <= 31; + case BitLength::BitLengthWord: + return index <= 15; + case BitLength::BitLengthByte: + return index <= 7; } + return false; +} - m_updatingCheckedStates = false; +String ^ CalculatorProgrammerBitFlipPanel::GenerateAutomationPropertiesName(int position, bool value) const +{ + auto resourceLoader = AppResourceProvider::GetInstance(); + + String ^ indexName = resourceLoader.GetResourceString(ref new Platform::String(to_wstring(position).c_str())); + String ^ automationNameTemplate = resourceLoader.GetResourceString(L"BitFlipItemAutomationName"); + String ^ bitPositionTemplate = resourceLoader.GetResourceString(L"BitPosition"); + + wstring bitPosition = LocalizationStringUtil::GetLocalizedString(bitPositionTemplate->Data(), indexName->Data()); + return ref new String(LocalizationStringUtil::GetLocalizedString(automationNameTemplate->Data(), bitPosition.c_str(), value ? L"1" : L"0").c_str()); } diff --git a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.h b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.h index 3ec6355d6..7ff77ea03 100644 --- a/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.h +++ b/src/Calculator/Views/CalculatorProgrammerBitFlipPanel.xaml.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // @@ -10,9 +10,6 @@ #include "Views/CalculatorProgrammerBitFlipPanel.g.h" #include "Controls/FlipButtons.h" -#include "Converters/BitFlipAutomationNameConverter.h" -#include "Converters/BooleanNegationConverter.h" -#include "Converters/VisibilityNegationConverter.h" #include "CalcViewModel/StandardCalculatorViewModel.h" namespace CalculatorApp @@ -22,6 +19,8 @@ namespace CalculatorApp public: CalculatorProgrammerBitFlipPanel(); + bool ShouldEnableBit(CalculatorApp::Common::BitLength length, int index); + property CalculatorApp::ViewModel::StandardCalculatorViewModel ^ Model { CalculatorApp::ViewModel::StandardCalculatorViewModel ^ get(); } @@ -34,15 +33,13 @@ namespace CalculatorApp void AssignFlipButtons(); - void SetVisibilityBinding( - _In_ Windows::UI::Xaml::FrameworkElement ^ element, - _In_ Platform::String ^ path, - _In_ Windows::UI::Xaml::Data::IValueConverter ^ converter); void OnBitToggled(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e); - void UpdateCheckedStates(); private: Windows::Foundation::EventRegistrationToken m_propertyChangedToken; + Platform::String ^ GenerateAutomationPropertiesName(int position, bool value) const; + void UpdateCheckedStates(bool updateAutomationPropertiesNames); + void UpdateAutomationPropertiesNames(); static const unsigned int s_numBits = 64; std::array m_flipButtons; diff --git a/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml index 1f1e443a4..5824eaeb6 100644 --- a/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml +++ b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml @@ -70,7 +70,6 @@ Grid.Column="1" Style="{StaticResource ProgKeypadRadioButtonStyle}" AutomationProperties.AutomationId="bitFlip" - Checked="ShowBitFlip" Content="" IsChecked="{x:Bind Model.IsBitFlipChecked, Mode=TwoWay}"/> diff --git a/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cpp b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cpp index e3b600bdc..b3da313cb 100644 --- a/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cpp +++ b/src/Calculator/Views/CalculatorProgrammerDisplayPanel.xaml.cpp @@ -6,6 +6,7 @@ #include "CalcViewModel/Common/TraceLogger.h" using namespace CalculatorApp; +using namespace CalculatorApp::Common; using namespace CalculatorApp::ViewModel; using namespace Platform; using namespace Windows::Foundation; @@ -26,14 +27,8 @@ CalculatorProgrammerDisplayPanel::CalculatorProgrammerDisplayPanel() InitializeComponent(); } -void CalculatorProgrammerDisplayPanel::ShowBitFlip(Object ^ sender, RoutedEventArgs ^ e) -{ - TraceLogger::GetInstance().LogBitFlipPaneClicked(); -} - void CalculatorProgrammerDisplayPanel::OnBitLengthButtonPressed(Object ^ parameter) { - TraceLogger::GetInstance().LogBitLengthButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); String ^ buttonId = parameter->ToString(); QwordButton->Visibility = ::Visibility::Collapsed; @@ -42,42 +37,29 @@ void CalculatorProgrammerDisplayPanel::OnBitLengthButtonPressed(Object ^ paramet ByteButton->Visibility = ::Visibility::Collapsed; if (buttonId == "0") { - Model->ButtonPressed->Execute(NumbersAndOperatorsEnum::Dword); + Model->ValueBitLength = BitLength::BitLengthDWord; DwordButton->Visibility = ::Visibility::Visible; DwordButton->Focus(::FocusState::Programmatic); - Model->IsQwordEnabled = false; - Model->IsDwordEnabled = true; - Model->IsWordEnabled = true; } else if (buttonId == "1") { - Model->ButtonPressed->Execute(NumbersAndOperatorsEnum::Word); + Model->ValueBitLength = BitLength::BitLengthWord; WordButton->Visibility = ::Visibility::Visible; WordButton->Focus(::FocusState::Programmatic); - Model->IsQwordEnabled = false; - Model->IsDwordEnabled = false; - Model->IsWordEnabled = true; } else if (buttonId == "2") { - Model->ButtonPressed->Execute(NumbersAndOperatorsEnum::Byte); + Model->ValueBitLength = BitLength::BitLengthByte; ByteButton->Visibility = ::Visibility::Visible; ByteButton->Focus(::FocusState::Programmatic); - Model->IsQwordEnabled = false; - Model->IsDwordEnabled = false; - Model->IsWordEnabled = false; } else if (buttonId == "3") { - Model->ButtonPressed->Execute(NumbersAndOperatorsEnum::Qword); + Model->ValueBitLength = BitLength::BitLengthQWord; QwordButton->Visibility = ::Visibility::Visible; QwordButton->Focus(::FocusState::Programmatic); - Model->IsQwordEnabled = true; - Model->IsDwordEnabled = true; - Model->IsWordEnabled = true; } - // update memory list according to bit length - Model->SetMemorizedNumbersString(); + } bool CalculatorProgrammerDisplayPanel::IsErrorVisualState::get() diff --git a/src/Calculator/Views/CalculatorProgrammerOperators.xaml.cpp b/src/Calculator/Views/CalculatorProgrammerOperators.xaml.cpp index ecaf1024e..6b2125c0c 100644 --- a/src/Calculator/Views/CalculatorProgrammerOperators.xaml.cpp +++ b/src/Calculator/Views/CalculatorProgrammerOperators.xaml.cpp @@ -38,7 +38,7 @@ CalculatorProgrammerOperators::CalculatorProgrammerOperators() void CalculatorProgrammerOperators::HexButtonChecked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - TraceLogger::GetInstance().LogRadixButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::HexButton, ViewMode::Programmer); if (Model) { Model->SwitchProgrammerModeBase(RADIX_TYPE::HEX_RADIX); @@ -47,7 +47,7 @@ void CalculatorProgrammerOperators::HexButtonChecked(_In_ Object ^ sender, _In_ void CalculatorProgrammerOperators::DecButtonChecked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - TraceLogger::GetInstance().LogRadixButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::DecButton, ViewMode::Programmer); if (Model) { Model->SwitchProgrammerModeBase(RADIX_TYPE::DEC_RADIX); @@ -56,7 +56,7 @@ void CalculatorProgrammerOperators::DecButtonChecked(_In_ Object ^ sender, _In_ void CalculatorProgrammerOperators::OctButtonChecked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - TraceLogger::GetInstance().LogRadixButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::OctButton, ViewMode::Programmer); if (Model) { Model->SwitchProgrammerModeBase(RADIX_TYPE::OCT_RADIX); @@ -65,7 +65,7 @@ void CalculatorProgrammerOperators::OctButtonChecked(_In_ Object ^ sender, _In_ void CalculatorProgrammerOperators::BinButtonChecked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - TraceLogger::GetInstance().LogRadixButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().UpdateButtonUsage(NumbersAndOperatorsEnum::BinButton, ViewMode::Programmer); if (Model) { Model->SwitchProgrammerModeBase(RADIX_TYPE::BIN_RADIX); diff --git a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml index 6cd455be9..eefbb757d 100644 --- a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml +++ b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml @@ -216,7 +216,7 @@ FontSize="16" AutomationProperties.AutomationId="shiftButton" Checked="Shift_Clicked" - Content="" + Content="" Unchecked="Shift_Clicked"/> + Content=""/> diff --git a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.cpp b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.cpp index b869bf689..7d067fe5e 100644 --- a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.cpp +++ b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.cpp @@ -27,9 +27,6 @@ CalculatorProgrammerRadixOperators::CalculatorProgrammerRadixOperators() : m_isErrorVisualState(false) { InitializeComponent(); - - auto booleanToVisibilityNegationConverter = ref new Converters::BooleanToVisibilityNegationConverter; - SetVisibilityBinding(ProgRadixOps, L"IsBinaryBitFlippingEnabled", booleanToVisibilityNegationConverter); } void CalculatorProgrammerRadixOperators::OnLoaded(Object ^, RoutedEventArgs ^) @@ -70,14 +67,6 @@ void CalculatorProgrammerRadixOperators::Shift_Clicked(Platform::Object ^ sender } } -void CalculatorProgrammerRadixOperators::SetVisibilityBinding(FrameworkElement ^ element, String ^ path, IValueConverter ^ converter) -{ - Binding ^ commandBinding = ref new Binding(); - commandBinding->Path = ref new PropertyPath(path); - commandBinding->Converter = converter; - element->SetBinding(VisibilityProperty, commandBinding); -} - void CalculatorProgrammerRadixOperators::ProgModeRadixChange() { NumberPad->ProgModeRadixChange(); diff --git a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.h b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.h index 83431f8ad..c0055e758 100644 --- a/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.h +++ b/src/Calculator/Views/CalculatorProgrammerRadixOperators.xaml.h @@ -32,7 +32,6 @@ namespace CalculatorApp private: void Shift_Clicked(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); - void SetVisibilityBinding(Windows::UI::Xaml::FrameworkElement ^ element, Platform::String ^ path, Windows::UI::Xaml::Data::IValueConverter ^ converter); void OnLoaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); void OnUnloaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); void ProgModeRadixChange(); diff --git a/src/Calculator/Views/CalculatorScientificAngleButtons.xaml.cpp b/src/Calculator/Views/CalculatorScientificAngleButtons.xaml.cpp index 66878e3f7..c6d4fd2be 100644 --- a/src/Calculator/Views/CalculatorScientificAngleButtons.xaml.cpp +++ b/src/Calculator/Views/CalculatorScientificAngleButtons.xaml.cpp @@ -37,7 +37,6 @@ CalculatorScientificAngleButtons::CalculatorScientificAngleButtons() void CalculatorScientificAngleButtons::HypButton_Toggled(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - TraceLogger::GetInstance().LogHypButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); } void CalculatorScientificAngleButtons::FToEButton_Toggled(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) @@ -48,7 +47,6 @@ void CalculatorScientificAngleButtons::FToEButton_Toggled(_In_ Object ^ sender, void CalculatorApp::CalculatorScientificAngleButtons::OnAngleButtonPressed(_In_ Object ^ commandParameter) { - TraceLogger::GetInstance().LogAngleButtonUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); String ^ buttonId = static_cast(commandParameter); DegreeButton->Visibility = ::Visibility::Collapsed; diff --git a/src/Calculator/Views/CalculatorScientificOperators.xaml b/src/Calculator/Views/CalculatorScientificOperators.xaml index 15f6600f0..e98ad4ea7 100644 --- a/src/Calculator/Views/CalculatorScientificOperators.xaml +++ b/src/Calculator/Views/CalculatorScientificOperators.xaml @@ -511,7 +511,7 @@ Style="{StaticResource SymbolOperatorButtonStyle}" AutomationProperties.AutomationId="squareRootButton" ButtonId="Sqrt" - Content=""/> + Content=""/> @@ -718,7 +718,7 @@ FontSize="16" AutomationProperties.AutomationId="negateButton" ButtonId="Negate" - Content="" + Content="" IsEnabled="{x:Bind Model.IsNegateEnabled, Mode=OneWay}"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -238,11 +292,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -279,7 +363,7 @@ Style="{StaticResource SymbolOperatorButtonStyle}" AutomationProperties.AutomationId="squareRootButton" ButtonId="Sqrt" - Content=""/> + Content=""/> + Content=""/> diff --git a/src/Calculator/Views/CalculatorStandardOperators.xaml.cpp b/src/Calculator/Views/CalculatorStandardOperators.xaml.cpp index 7ebd5928b..62dcec6f0 100644 --- a/src/Calculator/Views/CalculatorStandardOperators.xaml.cpp +++ b/src/Calculator/Views/CalculatorStandardOperators.xaml.cpp @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // diff --git a/src/Calculator/Views/CalculatorStandardOperators.xaml.h b/src/Calculator/Views/CalculatorStandardOperators.xaml.h index ebd71b998..3fb1069fc 100644 --- a/src/Calculator/Views/CalculatorStandardOperators.xaml.h +++ b/src/Calculator/Views/CalculatorStandardOperators.xaml.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // diff --git a/src/Calculator/Views/DateCalculator.xaml b/src/Calculator/Views/DateCalculator.xaml index e3899bbe1..37e007db1 100644 --- a/src/Calculator/Views/DateCalculator.xaml +++ b/src/Calculator/Views/DateCalculator.xaml @@ -358,7 +358,7 @@ - + diff --git a/src/Calculator/Views/DateCalculator.xaml.cpp b/src/Calculator/Views/DateCalculator.xaml.cpp index 33ed3dc39..5986bdf87 100644 --- a/src/Calculator/Views/DateCalculator.xaml.cpp +++ b/src/Calculator/Views/DateCalculator.xaml.cpp @@ -103,7 +103,7 @@ void DateCalculator::FromDate_DateChanged(_In_ CalendarDatePicker ^ sender, _In_ { auto dateCalcViewModel = safe_cast(this->DataContext); dateCalcViewModel->FromDate = e->NewDate->Value; - TraceLogger::GetInstance().LogDateDifferenceModeUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().LogDateCalculationModeUsed(false /* AddSubtractMode */); } else { @@ -117,7 +117,7 @@ void DateCalculator::ToDate_DateChanged(_In_ CalendarDatePicker ^ sender, _In_ C { auto dateCalcViewModel = safe_cast(this->DataContext); dateCalcViewModel->ToDate = e->NewDate->Value; - TraceLogger::GetInstance().LogDateDifferenceModeUsed(ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread())); + TraceLogger::GetInstance().LogDateCalculationModeUsed(false /* AddSubtractMode */); } else { @@ -131,8 +131,7 @@ void DateCalculator::AddSubtract_DateChanged(_In_ CalendarDatePicker ^ sender, _ { auto dateCalcViewModel = safe_cast(this->DataContext); dateCalcViewModel->StartDate = e->NewDate->Value; - TraceLogger::GetInstance().LogDateAddSubtractModeUsed( - ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()), dateCalcViewModel->IsAddMode); + TraceLogger::GetInstance().LogDateCalculationModeUsed(true /* AddSubtractMode */); } else { @@ -143,8 +142,11 @@ void DateCalculator::AddSubtract_DateChanged(_In_ CalendarDatePicker ^ sender, _ void CalculatorApp::DateCalculator::OffsetValue_Changed(_In_ Platform::Object ^ sender, _In_ SelectionChangedEventArgs ^ e) { auto dateCalcViewModel = safe_cast(this->DataContext); - TraceLogger::GetInstance().LogDateAddSubtractModeUsed( - ApplicationView::GetApplicationViewIdForWindow(CoreWindow::GetForCurrentThread()), dateCalcViewModel->IsAddMode); + // do not log diagnostics for no-ops and initialization of combo boxes + if (dateCalcViewModel->DaysOffset != 0 || dateCalcViewModel->MonthsOffset != 0 || dateCalcViewModel->YearsOffset != 0) + { + TraceLogger::GetInstance().LogDateCalculationModeUsed(true /* AddSubtractMode */); + } } void DateCalculator::OnCopyMenuItemClicked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) @@ -232,3 +234,9 @@ void DateCalculator::AddSubtractOption_Checked(_In_ Object ^ sender, _In_ Routed { RaiseLiveRegionChangedAutomationEvent(/* DateDiff mode */ false); } + +void CalculatorApp::DateCalculator::OnVisualStateChanged(Platform::Object ^ sender, Windows::UI::Xaml::VisualStateChangedEventArgs ^ e) +{ + auto state = std::wstring(e->NewState->Name->Begin()); + TraceLogger::GetInstance().LogVisualStateChanged(ViewMode::Date, state); +} diff --git a/src/Calculator/Views/DateCalculator.xaml.h b/src/Calculator/Views/DateCalculator.xaml.h index e158b9376..16c8bc34e 100644 --- a/src/Calculator/Views/DateCalculator.xaml.h +++ b/src/Calculator/Views/DateCalculator.xaml.h @@ -48,7 +48,9 @@ namespace CalculatorApp void OffsetDropDownClosed(_In_ Platform::Object ^ sender, _In_ Platform::Object ^ e); void CalendarFlyoutClosed(_In_ Platform::Object ^ sender, _In_ Platform::Object ^ e); void RaiseLiveRegionChangedAutomationEvent(_In_ bool isDateDiffMode); + void OnVisualStateChanged(Platform::Object ^ sender, Windows::UI::Xaml::VisualStateChangedEventArgs ^ e); Windows::Foundation::EventRegistrationToken m_dateCalcOptionChangedEventToken; + }; } diff --git a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp index ccfb1feba..bc828d778 100644 --- a/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp +++ b/src/Calculator/Views/GraphingCalculator/GraphingCalculator.xaml.cpp @@ -166,7 +166,7 @@ void GraphingCalculator::OnDataRequested(DataTransferManager^ sender, DataReques } catch(Exception ^ ex) { - TraceLogger::GetInstance().LogPlatformException(__FUNCTIONW__, ex); + TraceLogger::GetInstance().LogPlatformException(ViewMode::Graphing, __FUNCTIONW__, ex); // Something went wrong, notify the user. auto errorTitleString = resourceLoader->GetString(L"ShareActionErrorMessage"); diff --git a/src/Calculator/Views/HistoryList.xaml b/src/Calculator/Views/HistoryList.xaml index 329b42157..58f27a03d 100644 --- a/src/Calculator/Views/HistoryList.xaml +++ b/src/Calculator/Views/HistoryList.xaml @@ -49,7 +49,7 @@ Invoked="OnDeleteSwipeInvoked"/> - + @@ -57,9 +57,7 @@ - + + diff --git a/src/Calculator/Views/HistoryList.xaml.cpp b/src/Calculator/Views/HistoryList.xaml.cpp index 3dd7f301b..e9e74e4cd 100644 --- a/src/Calculator/Views/HistoryList.xaml.cpp +++ b/src/Calculator/Views/HistoryList.xaml.cpp @@ -42,11 +42,11 @@ HistoryList::HistoryList() void HistoryList::ListView_ItemClick(_In_ Object ^ sender, _In_ ItemClickEventArgs ^ e) { - HistoryViewModel ^ historyVM = static_cast(this->DataContext); - HistoryItemViewModel ^ clickedItem = safe_cast(e->ClickedItem); + HistoryViewModel^ historyVM = dynamic_cast(this->DataContext); + HistoryItemViewModel^ clickedItem = dynamic_cast(e->ClickedItem); // When the user clears the history list in the overlay view and presses enter, the clickedItem is nullptr - if (clickedItem != nullptr) + if (clickedItem != nullptr && historyVM != nullptr) { historyVM->ShowItem(clickedItem); } @@ -54,16 +54,21 @@ void HistoryList::ListView_ItemClick(_In_ Object ^ sender, _In_ ItemClickEventAr void HistoryList::OnDeleteMenuItemClicked(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) { - auto clickedItem = safe_cast(safe_cast(sender)->DataContext); - - Model->DeleteItem(clickedItem); + auto listViewItem = HistoryContextMenu->Target; + auto itemViewModel = dynamic_cast(HistoryListView->ItemFromContainer(listViewItem)); + if (itemViewModel != nullptr) + { + Model->DeleteItem(itemViewModel); + } } void HistoryList::OnDeleteSwipeInvoked(_In_ MUXC::SwipeItem ^ sender, _In_ MUXC::SwipeItemInvokedEventArgs ^ e) { - auto swipedItem = safe_cast(e->SwipeControl->DataContext); - - Model->DeleteItem(swipedItem); + auto swipedItem = dynamic_cast(e->SwipeControl->DataContext); + if (swipedItem != nullptr) + { + Model->DeleteItem(swipedItem); + } } void HistoryList::ScrollToBottom() diff --git a/src/Calculator/Views/MainPage.xaml b/src/Calculator/Views/MainPage.xaml index 063b7fd68..d7f8bf5db 100644 --- a/src/Calculator/Views/MainPage.xaml +++ b/src/Calculator/Views/MainPage.xaml @@ -32,6 +32,9 @@ + + + @@ -83,7 +86,23 @@ Command="{x:Bind Model.PasteCommand}"/> - + + + + + + + + + + + + + + + + + - + + + + - - - - - - - - - - - - - + Text="{x:Bind Model.CategoryName, Mode=OneWay}" + Visibility="{x:Bind Model.IsAlwaysOnTop, Converter={StaticResource BooleanToVisibilityNegationConverter}, Mode=OneWay}"/> +