diff --git a/.gitignore b/.gitignore index 415b87da..0167a23c 100644 --- a/.gitignore +++ b/.gitignore @@ -291,6 +291,9 @@ __pycache__/ Generated Files/ src/GraphControl/GraphingImplOverrides.props src/CalcViewModel/DataLoaders/DataLoaderConstants.h -!/build/config/TRexDefs/** -!src/Calculator/TemporaryKey.pfx -!src/CalculatorUnitTests/CalculatorUnitTests_TemporaryKey.pfx \ No newline at end of file +!src/Calculator/WindowsDev_TemporaryKey.pfx +!src/CalculatorUnitTests/WindowsDev_TemporaryKey.pfx +!src/x64 +!src/x86 +!src/out +!src/ARM \ No newline at end of file diff --git a/README.md b/README.md index 386e871d..08d59ee6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,10 @@ We also welcome [issues submitted on GitHub](https://github.com/Microsoft/calcul ## Roadmap For information regarding Windows Calculator plans and release schedule, please see the [Windows Calculator Roadmap](docs/Roadmap.md). +### Graphing Mode +Adding graphing calculator functionality [is on the project roadmap](https://github.com/Microsoft/calculator/issues/338) and we hope that this project can create a great end-user experience around graphing. To that end, the UI from the official in-box Windows Calculator is currently part of this repository, although the proprietary Microsoft-built graphing engine, which also drives graphing in Microsoft Mathematics and OneNote, is not. Community members can still be involved in the creation of the UI, however developer builds will not have graphing functionality due to the use of a [mock implementation of the engine](/src/GraphingImpl/Mocks) built on top of a +[common graphing API](/src/GraphingInterfaces). + ## 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. diff --git a/build/config/LocConfigPackageEs.xml b/build/config/LocConfigPackageEs.xml deleted file mode 100644 index d566c7cf..00000000 --- a/build/config/LocConfigPackageEs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/build/config/SignConfig.xml b/build/config/SignConfig.xml deleted file mode 100644 index eb298e87..00000000 --- a/build/config/SignConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/performance.xml b/build/config/TRexDefs/amd64/performance.xml deleted file mode 100644 index e6b6b40e..00000000 --- a/build/config/TRexDefs/amd64/performance.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - RS_APPS_VALIDATE AMD64 Desktop WinPerf Test Run - 134858 - paxeedev - true - true - Performance_AMD64.testlist - - - - VSTS\IsReliabilityRun - True - - - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs4_release.xml b/build/config/TRexDefs/amd64/rs4_release.xml deleted file mode 100644 index 78a52df9..00000000 --- a/build/config/TRexDefs/amd64/rs4_release.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - RS4_RELEASE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs4_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs5_release.xml b/build/config/TRexDefs/amd64/rs5_release.xml deleted file mode 100644 index 7fbccdc2..00000000 --- a/build/config/TRexDefs/amd64/rs5_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS5_RELEASE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs5_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/amd64/rs_apps_validate.xml b/build/config/TRexDefs/amd64/rs_apps_validate.xml deleted file mode 100644 index a90ca288..00000000 --- a/build/config/TRexDefs/amd64/rs_apps_validate.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - RS_APPS_VALIDATE AMD64 DesktopVM Test Run - 139642 - paxeedev - true - true - Desktop_AMD64.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - - RS_APPS_VALIDATE AMD64 WCOS Test Run - 153648 - paxeedev - true - true - WCOS_AMD64.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/arm/performance.xml b/build/config/TRexDefs/arm/performance.xml deleted file mode 100644 index 5fde2b8a..00000000 --- a/build/config/TRexDefs/arm/performance.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - RS_PRERELEASE ARM WindowsCore WinPerf Test Run - 135258 - paxeedev - true - true - Performance_ARM.testlist - - - - VSTS\IsReliabilityRun - True - - - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/arm/rs_apps_validate.xml b/build/config/TRexDefs/arm/rs_apps_validate.xml deleted file mode 100644 index 277c9761..00000000 --- a/build/config/TRexDefs/arm/rs_apps_validate.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs4_release.xml b/build/config/TRexDefs/x86/rs4_release.xml deleted file mode 100644 index 7f3f2cbf..00000000 --- a/build/config/TRexDefs/x86/rs4_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs4_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs5_release.xml b/build/config/TRexDefs/x86/rs5_release.xml deleted file mode 100644 index 205a9c09..00000000 --- a/build/config/TRexDefs/x86/rs5_release.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs5_release - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TRexDefs/x86/rs_apps_validate.xml b/build/config/TRexDefs/x86/rs_apps_validate.xml deleted file mode 100644 index 130dc8f7..00000000 --- a/build/config/TRexDefs/x86/rs_apps_validate.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - RS_APPS_VALIDATE x86 DesktopVM Test Run - 139643 - paxeedev - true - true - Desktop_x86.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - - - RS_PRERELEASE x86 WindowsCoreVM Test Run - 153715 - paxeedev - true - true - WCOS_x86.testlist - rs_apps_validate - - - AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - _AlternatePackageRoot - \\pkges\release\TAEF\validation.taef.provenance\1812.20007-develop\UniversalTestPackages;\\edge-svcs\Release\MITALite\Apps_eng.mitalite_ci\Latest.tst - - - - - \ No newline at end of file diff --git a/build/config/TestLists/ARM/Desktop_ARM.testlist b/build/config/TestLists/ARM/Desktop_ARM.testlist deleted file mode 100644 index cf0e5b66..00000000 --- a/build/config/TestLists/ARM/Desktop_ARM.testlist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "All" - }, - { - "FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm\\fre\\CalculatorUnitTests.testmd", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM/Performance_ARM.testlist b/build/config/TestLists/ARM/Performance_ARM.testlist deleted file mode 100644 index 2ec2d933..00000000 --- a/build/config/TestLists/ARM/Performance_ARM.testlist +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "Performance" - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM/WCOS_ARM.testlist b/build/config/TestLists/ARM/WCOS_ARM.testlist deleted file mode 100644 index cf0e5b66..00000000 --- a/build/config/TestLists/ARM/WCOS_ARM.testlist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "All" - }, - { - "FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm\\fre\\CalculatorUnitTests.testmd", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM64/Desktop_ARM64.testlist b/build/config/TestLists/ARM64/Desktop_ARM64.testlist deleted file mode 100644 index a2e620ca..00000000 --- a/build/config/TestLists/ARM64/Desktop_ARM64.testlist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm64\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "All" - }, - { - "FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm64\\fre\\CalculatorUnitTests.testmd", - } - ] -} \ No newline at end of file diff --git a/build/config/TestLists/ARM64/WCOS_ARM64.testlist b/build/config/TestLists/ARM64/WCOS_ARM64.testlist deleted file mode 100644 index a2e620ca..00000000 --- a/build/config/TestLists/ARM64/WCOS_ARM64.testlist +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "http://universaltest/schema/testlist-2.json", - "TestMDs": [ - { - "FilePath": "Calculator.UITests\\Prebuilt\\Test\\arm64\\fre\\Calculator.UITests.testmd", - "ExecutionProfile": "All" - }, - { - "FilePath": "CalculatorUnitTests\\Prebuilt\\Test\\arm64\\fre\\CalculatorUnitTests.testmd", - } - ] -} \ No newline at end of file diff --git a/build/config/rs_apps_utils.json b/build/config/rs_apps_utils.json deleted file mode 100644 index 9c16d817..00000000 --- a/build/config/rs_apps_utils.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Branch": [ - { - "collection":"microsoft", - "project":"OS", - "repo":"os", - "name":"official/rs_apps_utils", - "CheckinFiles": [ - { - "source":"vpack/app/calculator.app.man", - "path": "/redist/mspartners/ipa/Calculator", - "type": "File" - }, - { - "source":"vpack/app/calculator.app.man", - "path": "/onecoreuap/redist/mspartners/ipa/Calculator", - "type": "File" - } - ] - } - ], - "Email": [ - { - "sendTo":"paxeedev", - "sendOnErrorOnly": "True" - } - ] -} \ No newline at end of file diff --git a/build/config/rs_apps_validate.json b/build/config/rs_apps_validate.json deleted file mode 100644 index 3fbe98b5..00000000 --- a/build/config/rs_apps_validate.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "Branch": [ - { - "collection":"microsoft", - "project":"OS", - "repo":"os", - "name":"official/rs_apps_validate", - "CheckinFiles": [ - { - "source":"vpack/app/calculator.app.man", - "path": "/redist/mspartners/ipa/Calculator", - "type": "File" - }, - { - "source":"vpack/app/calculator.app.man", - "path": "/onecoreuap/redist/mspartners/ipa/Calculator", - "type": "File" - } - ] - } - ], - "Email": [ - { - "sendTo":"paxeedev", - "sendOnErrorOnly": "True" - } - ] -} \ No newline at end of file diff --git a/build/pipelines/azure-pipelines.ci.yaml b/build/pipelines/azure-pipelines.ci.yaml index 3f7faf16..2d55627a 100644 --- a/build/pipelines/azure-pipelines.ci.yaml +++ b/build/pipelines/azure-pipelines.ci.yaml @@ -6,11 +6,11 @@ trigger: - master -- servicing/* +- release/* - feature/* pr: - master -- servicing/* +- release/* - feature/* name: 0.$(Date:yyMM).$(DayOfMonth)$(Rev:rr).0 @@ -23,6 +23,7 @@ jobs: - template: ./templates/build-app-public.yaml parameters: platform: x86 + condition: not(eq(variables['Build.Reason'], 'PullRequest')) - template: ./templates/build-app-public.yaml parameters: diff --git a/build/pipelines/azure-pipelines.release.yaml b/build/pipelines/azure-pipelines.release.yaml index 33e10c63..f7c54551 100644 --- a/build/pipelines/azure-pipelines.release.yaml +++ b/build/pipelines/azure-pipelines.release.yaml @@ -4,13 +4,20 @@ # Store and the Windows image. This pipeline relies on Microsoft-internal resources to run. # +schedules: +- cron: "0 7 * * *" + displayName: Daily midnight build + branches: + include: + - master + trigger: none pr: none variables: versionMajor: 10 - versionMinor: 1910 - versionBuild: $[counter('10.1910.*', 0)] + versionMinor: 2012 + versionBuild: $[counter('10.2012.*', 0)] versionPatch: 0 name: '$(versionMajor).$(versionMinor).$(versionBuild).$(versionPatch)' @@ -30,11 +37,6 @@ jobs: platform: ARM condition: not(eq(variables['Build.Reason'], 'PullRequest')) -- template: ./templates/build-app-internal.yaml - parameters: - platform: ARM64 - condition: not(eq(variables['Build.Reason'], 'PullRequest')) - - template: ./templates/run-ui-tests.yaml parameters: platform: x64 @@ -54,5 +56,7 @@ jobs: platform: x86 - template: ./templates/package-appxbundle.yaml + parameters: + signBundle: true - template: ./templates/prepare-release-internalonly.yaml diff --git a/build/pipelines/templates/build-app-internal.yaml b/build/pipelines/templates/build-app-internal.yaml index 51eb1d28..9c55b30a 100644 --- a/build/pipelines/templates/build-app-internal.yaml +++ b/build/pipelines/templates/build-app-internal.yaml @@ -16,20 +16,18 @@ jobs: variables: BuildConfiguration: Release BuildPlatform: ${{ parameters.platform }} - workspace: - clean: outputs steps: - checkout: self - clean: true + fetchDepth: 1 - task: UniversalPackages@0 displayName: Download internals package inputs: command: download downloadDirectory: $(Build.SourcesDirectory) - vstsFeed: WindowsApps + vstsFeed: WindowsInboxApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.31 + vstsPackageVersion: 0.0.53 - template: ./build-single-architecture.yaml parameters: diff --git a/build/pipelines/templates/build-app-public.yaml b/build/pipelines/templates/build-app-public.yaml index c256e74d..70f18e5b 100644 --- a/build/pipelines/templates/build-app-public.yaml +++ b/build/pipelines/templates/build-app-public.yaml @@ -14,10 +14,8 @@ jobs: variables: BuildConfiguration: Release BuildPlatform: ${{ parameters.platform }} - workspace: - clean: outputs steps: - checkout: self - clean: true + fetchDepth: 1 - template: ./build-single-architecture.yaml \ No newline at end of file diff --git a/build/pipelines/templates/build-single-architecture.yaml b/build/pipelines/templates/build-single-architecture.yaml index a3e1d46c..49056df1 100644 --- a/build/pipelines/templates/build-single-architecture.yaml +++ b/build/pipelines/templates/build-single-architecture.yaml @@ -5,11 +5,10 @@ parameters: extraMsBuildArgs: '' steps: - - task: NuGetToolInstaller@0 - displayName: Use NuGet 5.0.2 + - task: NuGetToolInstaller@1 + displayName: Use NuGet 5.x inputs: - versionSpec: 5.0.2 - checkLatest: true + versionSpec: 5.x # In most accounts, you can just use 'NuGetCommand' instead of this GUID. # In the microsoft.visualstudio.com account, NuGetCommand is ambiguous so the GUID is needed. @@ -17,7 +16,7 @@ steps: displayName: NuGet restore src/Calculator.sln inputs: command: custom - arguments: restore src/Calculator.sln -Verbosity Detailed -NonInteractive + arguments: restore src/Calculator.sln -Verbosity Detailed - task: PowerShell@2 displayName: Set version number in AppxManifest @@ -33,7 +32,6 @@ steps: msbuildArgs: /bl:$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\Calculator.binlog /p:OutDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\ /p:GenerateProjectSpecificOutputFolder=true /p:AppVersion=$(Build.BuildNumber) /t:Publish /p:PublishDir=$(Build.BinariesDirectory)\$(BuildConfiguration)\$(BuildPlatform)\publish\ ${{ parameters.extraMsBuildArgs }} platform: $(BuildPlatform) configuration: $(BuildConfiguration) - clean: true maximumCpuCount: true - task: PublishBuildArtifacts@1 diff --git a/build/pipelines/templates/package-appxbundle.yaml b/build/pipelines/templates/package-appxbundle.yaml index 1e06d20d..f8de83ce 100644 --- a/build/pipelines/templates/package-appxbundle.yaml +++ b/build/pipelines/templates/package-appxbundle.yaml @@ -1,30 +1,29 @@ # This template contains a job which takes .appx packages which were built separately for each # architecture (arm, x86, etc.) and combines them into a single .appxbundle. +parameters: + signBundle: false + jobs: - job: Package dependsOn: - Buildx64 - Buildx86 - BuildARM - - BuildARM64 condition: | and ( in(dependencies.Buildx64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), in(dependencies.Buildx86.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), - in(dependencies.BuildARM.result, 'Succeeded', 'SucceededWithIssues', 'Skipped'), - in(dependencies.BuildARM64.result, 'Succeeded', 'SucceededWithIssues', 'Skipped') + in(dependencies.BuildARM.result, 'Succeeded', 'SucceededWithIssues', 'Skipped') ) pool: vmImage: windows-2019 - workspace: - clean: outputs variables: skipComponentGovernanceDetection: true steps: - checkout: self - clean: true + fetchDepth: 1 - task: DownloadBuildArtifacts@0 displayName: Download all .appx artifacts @@ -59,4 +58,44 @@ jobs: displayName: Publish AppxBundle artifact inputs: artifactName: appxBundle - pathToPublish: $(Build.ArtifactStagingDirectory)\appxBundle \ No newline at end of file + pathToPublish: $(Build.ArtifactStagingDirectory)\appxBundle + + - ${{ if eq(parameters.signBundle, true) }}: + - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: Send appxbundle to code signing service + inputs: + ConnectedServiceName: Essential Experiences Codesign + FolderPath: $(Build.ArtifactStagingDirectory)\appxBundle + Pattern: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "CertTemplateName": "WINMSAPP1ST", + "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", + "KeyCode": "Dynamic", + "OperationCode": "SigntoolvNextSign", + "Parameters": { + "OpusName": "Microsoft", + "OpusInfo": "http://www.microsoft.com", + "FileDigest": "/fd \"SHA256\"", + "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + }, + "ToolName": "sign", + "ToolVersion": "1.0" + }, + { + "CertTemplateName": "WINMSAPP1ST", + "CertSubjectName": "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", + "KeyCode": "Dynamic", + "OperationCode": "SigntoolvNextVerify", + "Parameters": {}, + "ToolName": "sign", + "ToolVersion": "1.0" + } + ] + - task: PublishBuildArtifacts@1 + displayName: Publish AppxBundleSigned artifact + inputs: + pathtoPublish: $(Build.ArtifactStagingDirectory)\appxBundle + artifactName: appxBundleSigned \ No newline at end of file diff --git a/build/pipelines/templates/prepare-release-internalonly.yaml b/build/pipelines/templates/prepare-release-internalonly.yaml index 9c7a45ea..b14d5979 100644 --- a/build/pipelines/templates/prepare-release-internalonly.yaml +++ b/build/pipelines/templates/prepare-release-internalonly.yaml @@ -2,8 +2,6 @@ # Windows using Microsoft-internal systems. It relies on Microsoft-internal resources and will not # work outside of Microsoft. # Specifically, this job: -# - Signs the bundle using a secure system. If you want to build your own, use SignTool following -# the example in the continuous integration pipeline. # - Builds VPacks for including the app in the Windows OS build. Azure DevOps Universal Packages # offers similar capabilities. # - Creates StoreBroker packages containing Microsoft Store assets. Although the Store assets for @@ -14,7 +12,7 @@ jobs: - job: WindowsInternalRelease dependsOn: Package pool: - name: Package ES Lab E + name: Package ES Standard Build workspace: clean: outputs variables: @@ -39,36 +37,21 @@ jobs: env: XES_DISABLEPROV: true - - task: NuGetToolInstaller@0 - displayName: Use NuGet 4.7.1 + - task: NuGetToolInstaller@1 + displayName: Use NuGet 5.x inputs: - versionSpec: 4.7.1 - checkLatest: true + versionSpec: 5.x - task: DownloadBuildArtifacts@0 - displayName: Download appxBundle artifact + displayName: Download appxBundleSigned artifact inputs: - artifactName: appxBundle - - - task: PkgESCodeSign@10 - displayName: Send bundle to Package ES code signing service - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - signConfigXml: build\config\SignConfig.xml - inPathRoot: $(Build.ArtifactStagingDirectory)\appxBundle - outPathRoot: $(Build.ArtifactStagingDirectory)\appxBundleSigned - - - task: PublishBuildArtifacts@1 - displayName: Publish AppxBundleSigned artifact - inputs: - pathtoPublish: $(Build.ArtifactStagingDirectory)\appxBundleSigned - artifactName: AppxBundleSigned + artifactName: appxBundleSigned - task: CopyFiles@2 displayName: Copy signed AppxBundle to vpack staging folder inputs: sourceFolder: $(Build.ArtifactStagingDirectory)\appxBundleSigned + contents: Microsoft.WindowsCalculator_8wekyb3d8bbwe.appxbundle targetFolder: $(Build.ArtifactStagingDirectory)\vpack\appxBundle - task: PkgESVPack@10 @@ -81,6 +64,8 @@ jobs: pushPkgName: calculator.app version: $(versionMajor).$(versionMinor).$(versionBuild) owner: paxeeapps + provData: false + - task: PublishBuildArtifacts@1 displayName: Publish vpack\app artifact with vpack manifest @@ -88,16 +73,14 @@ jobs: pathtoPublish: $(XES_VPACKMANIFESTDIRECTORY)\$(XES_VPACKMANIFESTNAME) artifactName: vpack\app - # TODO (macool): create and push internal test packages and test config - - task: UniversalPackages@0 displayName: Download internals package inputs: command: download downloadDirectory: $(Build.SourcesDirectory) - vstsFeed: WindowsApps + vstsFeed: WindowsInboxApps vstsFeedPackage: calculator-internals - vstsPackageVersion: 0.0.22 + vstsPackageVersion: 0.0.53 - powershell: | # Just modify this line to indicate where your en-us PDP file is. Leave the other lines alone. @@ -151,6 +134,8 @@ jobs: skipPolling: true targetPublishMode: Immediate logPath: '$(SBLogPath)' + deletePackages: true + numberOfPackagesToKeep: 0 - task: PkgESStoreBrokerAeroUpload@10 displayName: Upload to Aero flighting dashboard diff --git a/build/pipelines/templates/run-ui-tests.yaml b/build/pipelines/templates/run-ui-tests.yaml index 0e2d7656..347a3a8b 100644 --- a/build/pipelines/templates/run-ui-tests.yaml +++ b/build/pipelines/templates/run-ui-tests.yaml @@ -47,4 +47,6 @@ jobs: vsTestVersion: 16.0 runSettingsFile: $(Build.ArtifactStagingDirectory)/drop/Release/${{ parameters.platform }}/publish/${{ parameters.runsettingsFileName }} platform: ${{ parameters.platform }} - configuration: Release \ No newline at end of file + configuration: Release + ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: + testFiltercriteria: Priority=0 diff --git a/docs/ManualTests.md b/docs/ManualTests.md index ddc38ac1..884f0aef 100644 --- a/docs/ManualTests.md +++ b/docs/ManualTests.md @@ -260,17 +260,19 @@ Steps: 4. While in the Menu: Check the About Page *Expected: Everything in the about page fits into its window* 5. For Scientific Mode: At a Larger Scale -*Expected: All buttons are present and the up arrow is grayed out.* +*Expected: All buttons are present and the 2nd button is grayed out.* 6. For Scientific Mode: At a Smaller Scale -*Expected: All buttons are present and the up arrow is able to be toggled.* +*Expected: All buttons are present and the 2nd button is able to be toggled.* 7. For Programmer Mode: At a Any Scale -*Expected: All buttons are present and the up arrow is able to be toggled.* +*Expected: All buttons are present and the 2nd button is able to be toggled.* 8. For Converter Mode: While Scaling *Expected: The number pad and input areas move around each other gracefully.* -9. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default. -10. Set the system number format preference: Open a Run dialog (WIN + R) > type ‘intl.cpl’ > Enter > In the format dropdown list > Select Hebrew > Apply. -11. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build) -12. Repeat Steps 2-6 again in a (RTL) language. +9. For Graphing Mode: While Scaling +*Expected: The number pad, graph area, and input areas move around each other gracefully.* +10. Changing Language: Open Settings app > Time & language > Region & language > Add a language > Select a Right to Left (RTL) language such as Hebrew > Install the associated files> Set it to the system default. +11. Set the system number format preference: Open a Run dialog (WIN + R) > type ‘intl.cpl’ > Enter > In the format dropdown list > Select Hebrew > Apply. +12. Initiating the change: Package has completed installing > Sign out > Sign in. (This change to the app may also require a reinstallation of the build) +13. Repeat Steps 2-6 again in a (RTL) language. *Expected: No elements fall out of intended boundaries.* @@ -302,11 +304,60 @@ Verify the following: 11. "Bin" Binary: *Expected: A B C D E F 2 3 4 5 6 7 8 9 are inactive. A maximum of 64 characters can be entered.* +**Graphing Mode Test: Verify Graphing mode functions** +Steps: +1. Launch the "Calculator" app +2. Navigate to "Graphing" Calculator +3. Enter a function of x in the input field
+*Expected: Function is plotted in the graph area. Line color matches the colored square next to the input field* +4. Select the "+" button below the function input and enter more functions in the fields that appear
+*Expected: All functions are plotted in the graph area and match the colors of the input field squares* +5. Select the colored square for any function
+*Expected: Visibility of the function in the graph is toggled off/on* +6. Select the "Zoom In", "Zoom Out", and "Reset View' buttons in the graph area
+*Expected: Both X and Y axes zoom in, out, and revert to default settings, respectively* +7. Select the Trace button, then click + drag the graph until the red square is near a graphed function
+*Expected: Closest (X, Y) coordinates of the function to the red square are displayed with a black dot to indicate the location* +8. Enter "y=mx+b" into a function input field, then select "Variables" button
+*Expected: y=x+1 function is plotted in the graph, "Variables" modal window shows two variables "m" and "b" with values set to 1.* +9. Adjust the value, minimum, maximum, and step for each variable
+*Expected: y=mx+b graph adjusts to the new values for m and b, step size changes the increments of the slider for each value* +10. Share the graph via OneNote, Outlook/Mail, Twitter, and Feedback Hub
+*Expected: Modifiable message that contains an image of the graph customized for the chosen application opens* +11. Verify Key Graph Features tab shows the correct information for the following functions:
+ *(Note: IP = Inflection Points, VA = Vertical Asymptotes, HA = Horizontal Asymptotes, OA = Oblique Asymptotes)*
+ a. **y=x**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈ℝ⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing*
+ b. **y=1/x**
+ *Expected: Domain: ⁅𝑥≠0⁆; Range: ⁅y∈ℝ\{0}⁆; X/Y Intercepts: ø/ø; Max: none; Min: none; IP: none; VA: x=0; HA: y=0; OA: none; Parity: Odd; Monotonicity: (0, ∞) Decreasing, (-∞, 0) Increasing*
+ c. **y=x^2**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈{0, ∞)⁆; X/Y Intercepts: (0)/(0); Max: none; Min: (0,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0, ∞) Increasing, (-∞, 0) Decreasing*
+ d. **y=x^3**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈ℝ⁆; X/Y Intercepts: (0)/(0); Max: none; Min: none; IP: (0,0); VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: (-∞, ∞) Increasing*
+ e. **y=e^x**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅y∈(0, ∞)⁆; X/Y Intercepts: ø/(1); Max: none; Min: none; IP: none; VA: none; HA: y=0; OA: none; Parity: none; Monotonicity: (-∞, ∞) Increasing*
+ f. **y=ln(x)**
+ *Expected: Domain: ⁅𝑥>0⁆; Range: ⁅y∈ℝ⁆; X/Y Intercepts: (1)/ø; Max: none; Min: none; IP: none; VA: x=0; HA: none; OA: none; Parity: none; Monotonicity: (0, ∞) Increasing*
+ g. **y=sin(x)**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[−1,1]⁆; X/Y Intercepts: (⁅𝜋n1,n1∈ℤ⁆)/(0); Max: ⁅(2𝜋n1+𝜋/2,1),n1∈ℤ⁆; Min: ⁅(2𝜋n1+3𝜋/2,−1),n1∈ℤ⁆; IP: ⁅(𝜋n1,0),n1∈ℤ⁆; VA: none; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(2𝜋n1+𝜋/2,2𝜋n1+3𝜋/2),n1∈ℤ⁆ Decreasing; ⁅(2𝜋n1+3𝜋/2,2𝜋n1+5𝜋/2),n1∈ℤ⁆ Increasing; Period: 2𝜋*
+ h. **y=cos(x)**
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: ⁅𝑦∈[−1,1]⁆; X/Y Intercepts: (⁅𝜋n1+𝜋/2,n1∈ℤ⁆)/(1); Max: ⁅(2𝜋n1,1),n1∈ℤ⁆; Min: ⁅(2𝜋n1+𝜋,-1),n1∈ℤ⁆; IP: ⁅(𝜋n1+𝜋/2,0),n1∈ℤ⁆; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: ⁅(2𝜋n1+𝜋,2𝜋n1+2𝜋),n1∈ℤ⁆ Increasing, ⁅(2𝜋n1,2𝜋n1+𝜋),n1∈ℤ⁆ Decreasing; Period: 2𝜋*
+ i. **y=tan(x)**
+ *Expected: Domain: ⁅x≠𝜋n1+𝜋/2,∀n1∈ℤ⁆; Range: ⁅𝑦∈ℝ⁆; X/Y Intercepts: (x=𝜋n1, n1 ∈ℤ)/(0); Max: none; Min: none; IP: x=𝜋n1, n1 ∈ℤ; VA: x=𝜋n1+𝜋/2, n1∈ℤ; HA: none; OA: none; Parity: Odd; Monotonicity: ⁅(𝜋n1+𝜋/2,𝜋n1+3𝜋/2),n1∈ℤ⁆ Increasing; Period: 𝜋*
+ j. **y=sqrt(25-x^2)**
+ *Expected: Domain: ⁅x∈[-5,5]⁆; Range: ⁅𝑦∈[0,5]⁆; X/Y Intercepts: (5),(-5)/(5); Max: (0,5); Min: (-5,0) and (5,0); IP: none; VA: none; HA: none; OA: none; Parity: Even; Monotonicity: (0,5) Decreasing, (-5,0) Increasing*
+ k. **y=(-3x^2+2)/(x-1)**
+ *Expected: Domain: ⁅x≠1⁆; Range: ⁅𝑦∈(-∞, -2√3 - 6}U{2√3 -6,∞⁆; X/Y Intercepts: (-√6/3),(√6/3)/(-2); Max: ⁅(√3/3+1,-2√3−6)⁆; Min: ⁅(−√3/3+1,2√3−6)⁆; IP: none; VA: x=1; HA: none; OA: y=-3x-3; Parity: none; Monotonicity: (√3/3+1,∞) Decreasing, (1,√3/3+1,) Increasing(-√3/3+1,1), Increasing, (-∞,-√3/3+1) Decreasing*
+ l. **y=sin(sin(x))** ("too complex" error test)
+ *Expected: Domain: ⁅𝑥∈ℝ⁆; Range: Unable to calculate range for this function; X/Y Intercepts: none; Max: none; Min: none; IP: none; VA: none; HA: none; OA: none; Parity: odd; Monotonicity: Unable to determine the monotonicity of the function*
+ *These features are too complex for Calculator to calculate: Range, X Intercept, Period, Minima, Maxima, Inflection Points, Monotonicity* + m. **y=mx+b**
+ *Expected: Analysis is not supported for this function* **Date Calculation Test: Verify dates can be calculated.** Steps: -1. Launch the "Calculator" app. -2. Navigate to "Date Calculation" Calculator. +1. Launch the "Calculator" app +2. Navigate to "Date Calculation" Calculator 3. With "Difference between dates" Selected Change the various date input fields *Expected: From and To reflect dates input respectively.* @@ -332,80 +383,88 @@ Steps: 1. Launch the "Calculator" app. 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 +2. Press **Alt +1** to enter "Standard" mode *Expected: Move to "Standard" screen.* -3. Press **Alt +2** to Enter "Scientific" mode +3. Press **Alt +2** to enter "Scientific" mode *Expected: Move to "Scientific" screen.* -4. Press **Alt +3** to Enter "Programmer" mode +4. Press **Alt +3** to enter "Programmer" mode *Expected: Move to "Programming" screen.* -5. Press **Alt +4** to Enter "Date Calculation" mode +5. Press **Alt +4** to enter "Date Calculation" mode *Expected: Move to "Date Calculation" screen.* -6. Press **Ctrl +M** to Store in Memory -7. Press **Ctrl +P** to Add to Active Memory -8. Press **Ctrl +Q** to Subtract form Active Memory -9. Press **Ctrl +R** to Recall from Memory -10. Press **Ctrl +L** to Clear from Memory -11. Press **Delete** to Clear Current Input 'CE' -12. Press **Esc** to Full Clear Input 'C' -13. Press **F9** to Toggle '±' -14. Press **R** to Select '1/x' -15. Press **@** to Select '√' -16. Press **Ctrl + H** to Toggle History Panel +6 Press **Alt +5** to enter "Graphing" mode +*Expected: Move to "Graphing" screen.* +7. Press **Ctrl +M** to Store in Memory +8. Press **Ctrl +P** to Add to Active Memory +9. Press **Ctrl +Q** to Subtract form Active Memory +10. Press **Ctrl +R** to Recall from Memory +11. Press **Ctrl +L** to Clear from Memory +12. Press **Delete** to Clear Current Input 'CE' +13. Press **Esc** to Full Clear Input 'C' +14. Press **F9** to Toggle '±' +15. Press **R** to Select '1/x' +16. Press **@** to Select '√' +17. Press **Ctrl + H** to Toggle History Panel *Expected: Function when in small scale window.* -17. Press **Up arrow** to Move up History Panel +18. Press **Up arrow** to Move up History Panel *Expected: Function when in small scale window.* -18. Press **Down arrow** to Move Down History Panel +19. Press **Down arrow** to Move Down History Panel *Expected: Function when in small scale window.* -19. Press **Ctrl + Shift + D** to Clear History Panel +20. Press **Ctrl + Shift + D** to Clear History Panel *Expected: Function when in small scale window.* -20. Press **Spacebar** to Repeat Last Input +21. Press **Spacebar** to Repeat Last Input Verify the following in Scientific Mode -21. Press **F3** to Select 'DEG' -22. Press **F4** to Select 'RAD' -23. Press **F5** to Select 'GRAD' -24. Press **Ctrl +G** to Select '10ˣ' -25. Press **Ctrl +Y** to Select 'y√x' -26. Press **Shift +O** to Select 'sin-1' -27. Press **Shift + S** to Select 'cos-1' -28. Press **Shift +T** to Select 'tan-1' -29. Press **Ctrl +O** to Select 'Cosh' -30. Press **Ctrl +S** to Select 'Sinh' -31. Press **Ctrl +T** to Select 'Tanh' -32. Press **D** to Select 'Mod' -33. Press **L** to Select 'log' -34. Press **M** to Select 'dms' -35. Press **N** to Select 'ln' -36. Press **Ctrl +N** to Select 'ex' -37. Press **O** to Select 'Cos' -38. Press **P** to Select 'π' -39. Press **Q** to Select 'x²' -40. Press **S** to Select 'Sin' -41. Press **T** to Select 'Tan' -42. Press **V** to Toggle 'F-E' -43. Press **X** to Select 'Exp' -44. Press **Y** or **^** to Select 'xʸ' -45. Press **#** to Select 'x³' -46. Press **!** to Select 'n!' +22. Press **F3** to Select 'DEG' +23. Press **F4** to Select 'RAD' +24. Press **F5** to Select 'GRAD' +25. Press **Ctrl +G** to Select '10ˣ' +26. Press **Ctrl +Y** to Select 'y√x' +27. Press **Shift +O** to Select 'sin-1' +28. Press **Shift + S** to Select 'cos-1' +29. Press **Shift +T** to Select 'tan-1' +30. Press **Ctrl +O** to Select 'Cosh' +31. Press **Ctrl +S** to Select 'Sinh' +32. Press **Ctrl +T** to Select 'Tanh' +33. Press **D** to Select 'Mod' +34. Press **L** to Select 'log' +35. Press **M** to Select 'dms' +36. Press **N** to Select 'ln' +37. Press **Ctrl +N** to Select 'ex' +38. Press **O** to Select 'Cos' +39. Press **P** to Select 'π' +40. Press **Q** to Select 'x²' +41. Press **S** to Select 'Sin' +42. Press **T** to Select 'Tan' +43. Press **V** to Toggle 'F-E' +44. Press **X** to Select 'Exp' +45. Press **Y** or **^** to Select 'xʸ' +46. Press **#** to Select 'x³' +47. Press **!** to Select 'n!' Verify the following in Programmer Mode -47. Press **F2** to Select 'DWORD' -48. Press **F3** to Select 'WORD' -49. Press **F4** to Select 'BYTE' -50. Press **F5** to Select 'HEX' -51. Press **F6** to Select 'DEC' -52. Press **F7** to Select 'OCT' -53. Press **F8** to Select 'BIN' -54. Press **F12** to Select 'QWORD' -55. Press **A-F** to Input in HEX -56. Press **J** to Select 'RoL' -57. Press **K** to Select 'RoR' -58. Press **<** to Select 'Lsh' -59. Press **>** to Select 'Rsh' -60. Press **%** to Select 'Mod' -61. Press **|** to Select 'Or' -62. Press **~** to Select 'Not' -63. Press **&** to Select 'And' +48. Press **F2** to Select 'DWORD' +49. Press **F3** to Select 'WORD' +50. Press **F4** to Select 'BYTE' +51. Press **F5** to Select 'HEX' +52. Press **F6** to Select 'DEC' +53. Press **F7** to Select 'OCT' +54. Press **F8** to Select 'BIN' +55. Press **F12** to Select 'QWORD' +56. Press **A-F** to Input in HEX +57. Press **J** to Select 'RoL' +58. Press **K** to Select 'RoR' +59. Press **<** to Select 'Lsh' +60. Press **>** to Select 'Rsh' +61. Press **%** to Select 'Mod' +62. Press **|** to Select 'Or' +63. Press **~** to Select 'Not' +64. Press **&** to Select 'And' + + Verify the following in Graphing Mode +65. Press **x** to Select 'x' +66. Press **y** to Select 'y' +67. Press **Ctrl +[Numpad+]** to Select 'Zoom In' +68. Press **Ctrl +[Numpad-]** to Select 'Zoom Out' ## Localization Tests diff --git a/docs/Roadmap.md b/docs/Roadmap.md index 0b969c5c..59a4e547 100644 --- a/docs/Roadmap.md +++ b/docs/Roadmap.md @@ -4,15 +4,12 @@ Windows Calculator is under active development by Microsoft. ## Focus -In 2019, the Windows Calculator team is focused on: -* Refining our open source development process on GitHub -* Iterating upon the existing app design based on the latest [Fluent Design guidelines](https://developer.microsoft.com/en-us/windows/apps/design) -* Improving testing and diagnostics within the project -* Investigating new features with a focus on addressing top user feedback, including: - * Adding graphing mode - * Adding the ability for users to pin Calculator on top of other windows - * Providing additional customization options - * [Your feature idea here] - please review our [new feature development process](https://github.com/Microsoft/calculator/blob/master/docs/NewFeatureProcess.md) to get started! +In 2020, the Windows Calculator team is focused on: +* Iterating upon the existing app design based on the latest guidelines for [Fluent Design](https://developer.microsoft.com/en-us/windows/apps/design), [Windows 10X dual-screen devices](https://docs.microsoft.com/en-us/dual-screen/windows/), and [WinUI 3.0](https://github.com/microsoft/microsoft-ui-xaml). +* Unblocking community contributions by identifying and addressing bottlenecks affecting developers, including migrating portions of the codebase to C# ([#893](https://github.com/microsoft/calculator/issues/893)) and adding a settings page ([#596](https://github.com/microsoft/calculator/issues/596)). +* Addressing top user pain points, including clearing up confusion around how Standard Calculator behaves ([#138](https://github.com/microsoft/calculator/issues/138)) and fixing hidden characters in copied results ([#504](https://github.com/microsoft/calculator/issues/504)). +* Investigating unit converter improvements ([#379](https://github.com/microsoft/calculator/issues/379), [#589](https://github.com/microsoft/calculator/issues/589) and [#594](https://github.com/microsoft/calculator/issues/594)) +* [Your feature idea here] - please review our [new feature development process](https://github.com/Microsoft/calculator/blob/master/docs/NewFeatureProcess.md) to get started! We welcome contributions of all kinds from the community, but especially those that support the efforts above. Please see our [contributing guidelines](https://github.com/Microsoft/calculator/blob/master/CONTRIBUTING.md) for more information on how to get involved. diff --git a/nuget.config b/nuget.config index 0286336f..6ef7f342 100644 --- a/nuget.config +++ b/nuget.config @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index ef903269..00000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.13) -project(calculator CXX) -set(CMAKE_CXX_STANDARD 17) - -if(CMAKE_CXX_COMPILER MATCHES ".*clang") - # Clang disagress with libstdc++ about constexpr-ness of wstring_view - # See https://github.com/Microsoft/calculator/pull/321 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() - -add_subdirectory(CalcManager) diff --git a/src/CalcManager/CEngine/CMakeLists.txt b/src/CalcManager/CEngine/CMakeLists.txt deleted file mode 100644 index ba4348ff..00000000 --- a/src/CalcManager/CEngine/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -target_sources(CalcManager PRIVATE - calc.cpp - CalcInput.cpp - CalcUtils.cpp - History.cpp - Number.cpp - Rational.cpp - RationalMath.cpp - scicomm.cpp - scidisp.cpp - scifunc.cpp - scioper.cpp - sciset.cpp -) diff --git a/src/CalcManager/CEngine/History.cpp b/src/CalcManager/CEngine/History.cpp index d3a32ebb..ea73e18c 100644 --- a/src/CalcManager/CEngine/History.cpp +++ b/src/CalcManager/CEngine/History.cpp @@ -210,7 +210,7 @@ bool CHistoryCollector::FOpndAddedToHistory() // This is does the postfix to prefix translation of the input and adds the text to the history. Eg. doing 2 + 4 (sqrt), // this routine will ensure the last sqrt call unary operator, actually goes back in history and wraps 4 in sqrt(4) // -void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype) +void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, AngleType angletype) { int iCommandEnd; // When successfully applying a unary op, there should be an opnd already @@ -230,15 +230,15 @@ void CHistoryCollector::AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE a else { CalculationManager::Command angleOpCode; - if (angletype == ANGLE_DEG) + if (angletype == AngleType::Degrees) { angleOpCode = CalculationManager::Command::CommandDEG; } - else if (angletype == ANGLE_RAD) + else if (angletype == AngleType::Radians) { angleOpCode = CalculationManager::Command::CommandRAD; } - else // (angletype == ANGLE_GRAD) + else // (angletype == AngleType::Gradians) { angleOpCode = CalculationManager::Command::CommandGRAD; } diff --git a/src/CalcManager/CEngine/Rational.cpp b/src/CalcManager/CEngine/Rational.cpp index 8eb4a91a..c703f61d 100644 --- a/src/CalcManager/CEngine/Rational.cpp +++ b/src/CalcManager/CEngine/Rational.cpp @@ -460,7 +460,7 @@ namespace CalcEngine return !(lhs < rhs); } - wstring Rational::ToString(uint32_t radix, NUMOBJ_FMT fmt, int32_t precision) const + wstring Rational::ToString(uint32_t radix, NumberFormat fmt, int32_t precision) const { PRAT rat = this->ToPRAT(); wstring result{}; diff --git a/src/CalcManager/CEngine/RationalMath.cpp b/src/CalcManager/CEngine/RationalMath.cpp index 4b1a4b8a..00b628ab 100644 --- a/src/CalcManager/CEngine/RationalMath.cpp +++ b/src/CalcManager/CEngine/RationalMath.cpp @@ -147,7 +147,7 @@ Rational RationalMath::Abs(Rational const& rat) return Rational{ Number{ 1, rat.P().Exp(), rat.P().Mantissa() }, Number{ 1, rat.Q().Exp(), rat.Q().Mantissa() } }; } -Rational RationalMath::Sin(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::Sin(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); @@ -167,7 +167,7 @@ Rational RationalMath::Sin(Rational const& rat, ANGLE_TYPE angletype) return result; } -Rational RationalMath::Cos(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::Cos(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); @@ -187,7 +187,7 @@ Rational RationalMath::Cos(Rational const& rat, ANGLE_TYPE angletype) return result; } -Rational RationalMath::Tan(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::Tan(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); @@ -207,7 +207,7 @@ Rational RationalMath::Tan(Rational const& rat, ANGLE_TYPE angletype) return result; } -Rational RationalMath::ASin(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::ASin(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); @@ -227,7 +227,7 @@ Rational RationalMath::ASin(Rational const& rat, ANGLE_TYPE angletype) return result; } -Rational RationalMath::ACos(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::ACos(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); @@ -247,7 +247,7 @@ Rational RationalMath::ACos(Rational const& rat, ANGLE_TYPE angletype) return result; } -Rational RationalMath::ATan(Rational const& rat, ANGLE_TYPE angletype) +Rational RationalMath::ATan(Rational const& rat, AngleType angletype) { PRAT prat = rat.ToPRAT(); diff --git a/src/CalcManager/CEngine/calc.cpp b/src/CalcManager/CEngine/calc.cpp index c5775379..dd70fb63 100644 --- a/src/CalcManager/CEngine/calc.cpp +++ b/src/CalcManager/CEngine/calc.cpp @@ -73,7 +73,7 @@ CCalcEngine::CCalcEngine( , m_bRecord(false) , m_bSetCalcState(false) , m_input(DEFAULT_DEC_SEPARATOR) - , m_nFE(FMT_FLOAT) + , m_nFE(NumberFormat::Float) , m_memoryValue{ make_unique() } , m_holdVal{} , m_currentVal{} @@ -94,8 +94,8 @@ CCalcEngine::CCalcEngine( , m_nPrecOp() , m_precedenceOpCount(0) , m_nLastCom(0) - , m_angletype(ANGLE_DEG) - , m_numwidth(QWORD_WIDTH) + , m_angletype(AngleType::Degrees) + , m_numwidth(NUM_WIDTH::QWORD_WIDTH) , m_HistoryCollector(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR) , m_groupSeparator(DEFAULT_GRP_SEPARATOR) { @@ -105,7 +105,7 @@ CCalcEngine::CCalcEngine( m_maxTrigonometricNum = RationalMath::Pow(10, 100); - SetRadixTypeAndNumWidth(DEC_RADIX, m_numwidth); + SetRadixTypeAndNumWidth(RadixType::Decimal, m_numwidth); SettingsChanged(); DisplayNum(); } @@ -128,10 +128,20 @@ void CCalcEngine::InitChopNumbers() auto maxVal = m_chopNumbers[i] / 2; maxVal = RationalMath::Integer(maxVal); - m_maxDecimalValueStrings[i] = maxVal.ToString(10, FMT_FLOAT, m_precision); + m_maxDecimalValueStrings[i] = maxVal.ToString(10, NumberFormat::Float, m_precision); } } +CalcEngine::Rational CCalcEngine::GetChopNumber() const +{ + return m_chopNumbers[static_cast(m_numwidth)]; +} + +std::wstring CCalcEngine::GetMaxDecimalValueString() const +{ + return m_maxDecimalValueStrings[static_cast(m_numwidth)]; +} + // Gets the number in memory for UI to keep it persisted and set it again to a different instance // of CCalcEngine. Otherwise it will get destructed with the CalcEngine unique_ptr CCalcEngine::PersistedMemObject() diff --git a/src/CalcManager/CEngine/scicomm.cpp b/src/CalcManager/CEngine/scicomm.cpp index f7509ab3..20e60a29 100644 --- a/src/CalcManager/CEngine/scicomm.cpp +++ b/src/CalcManager/CEngine/scicomm.cpp @@ -35,19 +35,16 @@ namespace IDC_ADD,2, IDC_SUB,2, IDC_RSHF,3, IDC_LSHF,3, IDC_RSHFL,3, IDC_MOD,3, IDC_DIV,3, IDC_MUL,3, - IDC_PWR,4, IDC_ROOT,4, IDC_LOGBASEX,4 }; - unsigned int iPrec; + IDC_PWR,4, IDC_ROOT,4, IDC_LOGBASEY,4 }; - iPrec = 0; - while ((iPrec < size(rgbPrec)) && (nopCode != rgbPrec[iPrec])) + for (unsigned int iPrec = 0; iPrec < size(rgbPrec); iPrec += 2) { - iPrec += 2; + if (nopCode == rgbPrec[iPrec]) + { + return rgbPrec[iPrec + 1]; + } } - if (iPrec >= size(rgbPrec)) - { - iPrec = 0; - } - return rgbPrec[iPrec + 1]; + return 0; } } @@ -104,8 +101,6 @@ void CCalcEngine::ProcessCommand(OpCode wParam) void CCalcEngine::ProcessCommandWorker(OpCode wParam) { - int nx, ni; - // Save the last command. Some commands are not saved in this manor, these // commands are: // Inv, Deg, Rad, Grad, Stat, FE, MClear, Back, and Exp. The excluded @@ -163,14 +158,11 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) DisplayNum(); // Causes 3.000 to shrink to 3. on first op. } } - else + else if (IsDigitOpCode(wParam) || wParam == IDC_PNT) { - if (IsDigitOpCode(wParam) || wParam == IDC_PNT) - { - m_bRecord = true; - m_input.Clear(); - CheckAndAddLastBinOpToHistory(); - } + m_bRecord = true; + m_input.Clear(); + CheckAndAddLastBinOpToHistory(); } // Interpret digit keys. @@ -185,7 +177,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) return; } - if (!m_input.TryAddDigit(iValue, m_radix, m_fIntegerMode, m_maxDecimalValueStrings[m_numwidth], m_dwWordBitWidth, m_cIntDigitsSav)) + if (!m_input.TryAddDigit(iValue, m_radix, m_fIntegerMode, GetMaxDecimalValueString(), m_dwWordBitWidth, m_cIntDigitsSav)) { HandleErrorCommand(wParam); HandleMaxDigitsReached(); @@ -203,7 +195,6 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) // Change the operation if last input was operation. if (IsBinOpCode(m_nLastCom)) { - int nPrev; bool fPrecInvToHigher = false; // Is Precedence Inversion from lower to higher precedence happening ?? m_nOpCode = (int)wParam; @@ -214,9 +205,9 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) // Here * is m_nPrevOpCode, m_currentVal is 2 (by 1*2), m_nLastCom is +, m_nOpCode is ^ if (m_fPrecedence && 0 != m_nPrevOpCode) { - nPrev = NPrecedenceOfOp(m_nPrevOpCode); - nx = NPrecedenceOfOp(m_nLastCom); - ni = NPrecedenceOfOp(m_nOpCode); + int nPrev = NPrecedenceOfOp(m_nPrevOpCode); + int nx = NPrecedenceOfOp(m_nLastCom); + int ni = NPrecedenceOfOp(m_nOpCode); if (nx <= nPrev && ni > nPrev) // condition for Precedence Inversion { fPrecInvToHigher = true; @@ -243,8 +234,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) { DoPrecedenceCheckAgain: - nx = NPrecedenceOfOp((int)wParam); - ni = NPrecedenceOfOp(m_nOpCode); + int nx = NPrecedenceOfOp((int)wParam); + int ni = NPrecedenceOfOp(m_nOpCode); if ((nx > ni) && m_fPrecedence) { @@ -492,8 +483,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) m_lastVal = m_precedenceVals[m_precedenceOpCount]; // Precedence Inversion check - ni = NPrecedenceOfOp(m_nPrevOpCode); - nx = NPrecedenceOfOp(m_nOpCode); + int ni = NPrecedenceOfOp(m_nPrevOpCode); + int nx = NPrecedenceOfOp(m_nOpCode); if (ni <= nx) { m_HistoryCollector.EnclosePrecInversionBrackets(); @@ -518,20 +509,15 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) case IDC_OPENP: case IDC_CLOSEP: - nx = 0; - if (wParam == IDC_OPENP) - { - nx = 1; - } // -IF- the Paren holding array is full and we try to add a paren // -OR- the paren holding array is empty and we try to remove a // paren // -OR- the precedence holding array is full - if ((m_openParenCount >= MAXPRECDEPTH && nx) || (!m_openParenCount && !nx) + if ((m_openParenCount >= MAXPRECDEPTH && (wParam == IDC_OPENP)) || (!m_openParenCount && (wParam != IDC_OPENP)) || ((m_precedenceOpCount >= MAXPRECDEPTH && m_nPrecOp[m_precedenceOpCount - 1] != 0))) { - if (!m_openParenCount && !nx) + if (!m_openParenCount && (wParam != IDC_OPENP)) { m_pCalcDisplay->OnNoRightParenAdded(); } @@ -540,7 +526,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) break; } - if (nx) + if (wParam == IDC_OPENP) { CheckAndAddLastBinOpToHistory(); m_HistoryCollector.AddOpenBraceToHistory(); @@ -588,8 +574,8 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) for (m_nOpCode = m_nPrecOp[--m_precedenceOpCount]; m_nOpCode; m_nOpCode = m_nPrecOp[--m_precedenceOpCount]) { // Precedence Inversion check - ni = NPrecedenceOfOp(m_nPrevOpCode); - nx = NPrecedenceOfOp(m_nOpCode); + int ni = NPrecedenceOfOp(m_nPrevOpCode); + int nx = NPrecedenceOfOp(m_nOpCode); if (ni <= nx) { m_HistoryCollector.EnclosePrecInversionBrackets(); @@ -633,7 +619,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) case IDM_OCT: case IDM_BIN: { - SetRadixTypeAndNumWidth((RADIX_TYPE)(wParam - IDM_HEX), (NUM_WIDTH)-1); + SetRadixTypeAndNumWidth((RadixType)(wParam - IDM_HEX), (NUM_WIDTH)-1); m_HistoryCollector.UpdateHistoryExpression(m_radix, m_precision); break; } @@ -649,20 +635,20 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) } // Compat. mode BaseX: Qword, Dword, Word, Byte - SetRadixTypeAndNumWidth((RADIX_TYPE)-1, (NUM_WIDTH)(wParam - IDM_QWORD)); + SetRadixTypeAndNumWidth((RadixType)-1, (NUM_WIDTH)(wParam - IDM_QWORD)); break; case IDM_DEG: case IDM_RAD: case IDM_GRAD: - m_angletype = static_cast(wParam - IDM_DEG); + m_angletype = static_cast(wParam - IDM_DEG); break; case IDC_SIGN: { if (m_bRecord) { - if (m_input.TryToggleSign(m_fIntegerMode, m_maxDecimalValueStrings[m_numwidth])) + if (m_input.TryToggleSign(m_fIntegerMode, GetMaxDecimalValueString())) { DisplayNum(); } @@ -780,7 +766,7 @@ void CCalcEngine::ProcessCommandWorker(OpCode wParam) break; case IDC_FE: // Toggle exponential notation display. - m_nFE = NUMOBJ_FMT(!(int)m_nFE); + m_nFE = NumberFormat(!(int)m_nFE); DisplayNum(); break; @@ -952,7 +938,7 @@ static const std::unordered_map operatorStringTable = { IDC_SECH, { SIDS_SECH, SIDS_ASECH } }, { IDC_CSCH, { SIDS_CSCH, SIDS_ACSCH } }, { IDC_COTH, { SIDS_COTH, SIDS_ACOTH } }, - + { IDC_LN, { L"", SIDS_POWE } }, { IDC_SQR, { SIDS_SQR } }, { IDC_CUB, { SIDS_CUBE } }, @@ -962,7 +948,7 @@ static const std::unordered_map operatorStringTable = { IDC_SIGN, { SIDS_NEGATE } }, { IDC_DEGREES, { SIDS_DEGREES } }, { IDC_POW2, { SIDS_TWOPOWX } }, - { IDC_LOGBASEX, { SIDS_LOGBASEX } }, + { IDC_LOGBASEY, { SIDS_LOGBASEY } }, { IDC_ABS, { SIDS_ABS } }, { IDC_CEIL, { SIDS_CEIL } }, { IDC_FLOOR, { SIDS_FLOOR } }, @@ -971,11 +957,11 @@ static const std::unordered_map operatorStringTable = { IDC_RSHFL, { SIDS_RSH } }, { IDC_RORC, { SIDS_ROR } }, { IDC_ROLC, { SIDS_ROL } }, - { IDC_CUBEROOT, {SIDS_CUBEROOT} }, - { IDC_MOD, {SIDS_MOD, L"", L"", L"", L"", L"", SIDS_PROGRAMMER_MOD} }, + { IDC_CUBEROOT, { SIDS_CUBEROOT } }, + { IDC_MOD, { SIDS_MOD, L"", L"", L"", L"", L"", SIDS_PROGRAMMER_MOD } }, }; -wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype) +wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype) { // Try to lookup the ID in the UFNE table wstring ids = L""; @@ -983,7 +969,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE if (auto pair = operatorStringTable.find(nOpCode); pair != operatorStringTable.end()) { const FunctionNameElement& element = pair->second; - if (!element.hasAngleStrings || ANGLE_DEG == angletype) + if (!element.hasAngleStrings || AngleType::Degrees == angletype) { if (fInv) { @@ -995,7 +981,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE ids = element.degreeString; } } - else if (ANGLE_RAD == angletype) + else if (AngleType::Radians == angletype) { if (fInv) { @@ -1006,7 +992,7 @@ wstring_view CCalcEngine::OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE ids = element.radString; } } - else if (ANGLE_GRAD == angletype) + else if (AngleType::Gradians == angletype) { if (fInv) { @@ -1059,7 +1045,7 @@ bool CCalcEngine::IsCurrentTooBigForTrig() return m_currentVal >= m_maxTrigonometricNum; } -int CCalcEngine::GetCurrentRadix() +uint32_t CCalcEngine::GetCurrentRadix() { return m_radix; } @@ -1108,7 +1094,7 @@ wstring CCalcEngine::GetStringForDisplay(Rational const& rat, uint32_t radix) if ((radix == 10) && fMsb) { // If high bit is set, then get the decimal number in negative 2's complement form. - tempRat = -((tempRat ^ m_chopNumbers[m_numwidth]) + 1); + tempRat = -((tempRat ^ GetChopNumber()) + 1); } result = tempRat.ToString(radix, m_nFE, m_precision); diff --git a/src/CalcManager/CEngine/scidisp.cpp b/src/CalcManager/CEngine/scidisp.cpp index 632969fd..0074c77a 100644 --- a/src/CalcManager/CEngine/scidisp.cpp +++ b/src/CalcManager/CEngine/scidisp.cpp @@ -67,10 +67,10 @@ CalcEngine::Rational CCalcEngine::TruncateNumForIntMath(CalcEngine::Rational con { // if negative make positive by doing a twos complement result = -(result)-1; - result ^= m_chopNumbers[m_numwidth]; + result ^= GetChopNumber(); } - result &= m_chopNumbers[m_numwidth]; + result &= GetChopNumber(); return result; } @@ -85,7 +85,7 @@ void CCalcEngine::DisplayNum(void) // called. // if (m_bRecord || gldPrevious.value != m_currentVal || gldPrevious.precision != m_precision || gldPrevious.radix != m_radix || gldPrevious.nFE != (int)m_nFE - || gldPrevious.bUseSep != true || gldPrevious.numwidth != m_numwidth || gldPrevious.fIntMath != m_fIntegerMode || gldPrevious.bRecord != m_bRecord) + || !gldPrevious.bUseSep || gldPrevious.numwidth != m_numwidth || gldPrevious.fIntMath != m_fIntegerMode || gldPrevious.bRecord != m_bRecord) { gldPrevious.precision = m_precision; gldPrevious.radix = m_radix; diff --git a/src/CalcManager/CEngine/scifunc.cpp b/src/CalcManager/CEngine/scifunc.cpp index 15ef164b..b767968a 100644 --- a/src/CalcManager/CEngine/scifunc.cpp +++ b/src/CalcManager/CEngine/scifunc.cpp @@ -42,7 +42,7 @@ CalcEngine::Rational CCalcEngine::SciCalcFunctions(CalcEngine::Rational const& r } else { - result = rat ^ m_chopNumbers[m_numwidth]; + result = rat ^ GetChopNumber(); } break; diff --git a/src/CalcManager/CEngine/scioper.cpp b/src/CalcManager/CEngine/scioper.cpp index 6ea3956b..fdb9a7c0 100644 --- a/src/CalcManager/CEngine/scioper.cpp +++ b/src/CalcManager/CEngine/scioper.cpp @@ -29,11 +29,11 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa break; case IDC_NAND: - result = (result & rhs) ^ m_chopNumbers[m_numwidth]; + result = (result & rhs) ^ GetChopNumber(); break; case IDC_NOR: - result = (result | rhs) ^ m_chopNumbers[m_numwidth]; + result = (result | rhs) ^ GetChopNumber(); break; case IDC_RSHF: @@ -53,10 +53,10 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa { result = Integer(result); - auto tempRat = m_chopNumbers[m_numwidth] >> holdVal; + auto tempRat = GetChopNumber() >> holdVal; tempRat = Integer(tempRat); - result |= tempRat ^ m_chopNumbers[m_numwidth]; + result |= tempRat ^ GetChopNumber(); } break; } @@ -105,7 +105,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa if (fMsb) { - result = (rhs ^ m_chopNumbers[m_numwidth]) + 1; + result = (rhs ^ GetChopNumber()) + 1; iNumeratorSign = -1; } @@ -115,7 +115,7 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa if (fMsb) { - temp = (temp ^ m_chopNumbers[m_numwidth]) + 1; + temp = (temp ^ GetChopNumber()) + 1; iDenominatorSign = -1; } @@ -158,8 +158,8 @@ CalcEngine::Rational CCalcEngine::DoOperation(int operation, CalcEngine::Rationa result = Root(rhs, result); break; - case IDC_LOGBASEX: - result = (Log(result) / Log(rhs)); + case IDC_LOGBASEY: + result = (Log(rhs) / Log(result)); break; } } diff --git a/src/CalcManager/CEngine/sciset.cpp b/src/CalcManager/CEngine/sciset.cpp index 676ab0eb..cb143559 100644 --- a/src/CalcManager/CEngine/sciset.cpp +++ b/src/CalcManager/CEngine/sciset.cpp @@ -9,7 +9,7 @@ using namespace std; // To be called when either the radix or num width changes. You can use -1 in either of these values to mean // dont change that. -void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth) +void CCalcEngine::SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth) { // When in integer mode, the number is represented in 2's complement form. When a bit width is changing, we can // change the number representation back to sign, abs num form in ratpak. Soon when display sees this, it will @@ -24,19 +24,19 @@ void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwid if (fMsb) { // If high bit is set, then get the decimal number in -ve 2'scompl form. - auto tempResult = m_currentVal ^ m_chopNumbers[m_numwidth]; + auto tempResult = m_currentVal ^ GetChopNumber(); m_currentVal = -(tempResult + 1); } } - if (radixtype >= HEX_RADIX && radixtype <= BIN_RADIX) + if (radixtype >= RadixType::Hex && radixtype <= RadixType::Binary) { m_radix = NRadixFromRadixType(radixtype); // radixtype is not even saved } - if (numwidth >= QWORD_WIDTH && numwidth <= BYTE_WIDTH) + if (numwidth >= NUM_WIDTH::QWORD_WIDTH && numwidth <= NUM_WIDTH::BYTE_WIDTH) { m_numwidth = numwidth; m_dwWordBitWidth = DwWordBitWidthFromeNumWidth(numwidth); @@ -50,29 +50,36 @@ void CCalcEngine::SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwid DisplayNum(); } -int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH /*numwidth*/) +int32_t CCalcEngine::DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth) { - static constexpr int nBitMax[] = { 64, 32, 16, 8 }; - int32_t wmax = nBitMax[0]; - - if (m_numwidth >= 0 && (size_t)m_numwidth < size(nBitMax)) + switch (numwidth) { - wmax = nBitMax[m_numwidth]; + case NUM_WIDTH::DWORD_WIDTH: + return 32; + case NUM_WIDTH::WORD_WIDTH: + return 16; + case NUM_WIDTH::BYTE_WIDTH: + return 8; + case NUM_WIDTH::QWORD_WIDTH: + default: + return 64; } - return wmax; } -uint32_t CCalcEngine::NRadixFromRadixType(RADIX_TYPE radixtype) +uint32_t CCalcEngine::NRadixFromRadixType(RadixType radixtype) { - static constexpr uint32_t rgnRadish[4] = { 16, 10, 8, 2 }; /* Number bases in the same order as radixtype */ - uint32_t radix = 10; - - // convert special bases into symbolic values - if (radixtype >= 0 && (size_t)radixtype < size(rgnRadish)) + switch (radixtype) { - radix = rgnRadish[radixtype]; + case RadixType::Hex: + return 16; + case RadixType::Octal: + return 8; + case RadixType::Binary: + return 2; + case RadixType::Decimal: + default: + return 10; } - return radix; } // Toggles a given bit into the number representation. returns true if it changed it actually. @@ -141,7 +148,7 @@ void CCalcEngine::UpdateMaxIntDigits() // if in integer mode you still have to honor the max digits you can enter based on bit width if (m_fIntegerMode) { - m_cIntDigitsSav = static_cast(m_maxDecimalValueStrings[m_numwidth].length()) - 1; + m_cIntDigitsSav = static_cast(GetMaxDecimalValueString().length()) - 1; // This is the max digits you can enter a decimal in fixed width mode aka integer mode -1. The last digit // has to be checked separately } diff --git a/src/CalcManager/CMakeLists.txt b/src/CalcManager/CMakeLists.txt deleted file mode 100644 index c02bc78d..00000000 --- a/src/CalcManager/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -add_library(CalcManager - CalculatorHistory.cpp - CalculatorManager.cpp - ExpressionCommand.cpp - pch.cpp - UnitConverter.cpp -) -target_include_directories(CalcManager PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -add_subdirectory(Ratpack) -add_subdirectory(CEngine) diff --git a/src/CalcManager/CalcManager.vcxproj b/src/CalcManager/CalcManager.vcxproj index b30b3976..80940c6a 100644 --- a/src/CalcManager/CalcManager.vcxproj +++ b/src/CalcManager/CalcManager.vcxproj @@ -132,29 +132,9 @@ - - false - - - false - - - false - - - false - - - false - - - false - - - false - - + false + true @@ -366,4 +346,4 @@ - \ No newline at end of file + diff --git a/src/CalcManager/CalculatorHistory.cpp b/src/CalcManager/CalculatorHistory.cpp index 6c9a002b..467cd420 100644 --- a/src/CalcManager/CalculatorHistory.cpp +++ b/src/CalcManager/CalculatorHistory.cpp @@ -41,21 +41,13 @@ unsigned int CalculatorHistory::AddToHistory( _In_ shared_ptr>> const& commands, wstring_view result) { - unsigned int addedIndex; shared_ptr spHistoryItem = make_shared(); spHistoryItem->historyItemVector.spTokens = tokens; spHistoryItem->historyItemVector.spCommands = commands; - - // to be changed when pszexp is back - wstring generatedExpression = GetGeneratedExpression(*tokens); - // Prefixing and suffixing the special Unicode markers to ensure that the expression - // in the history doesn't get broken for RTL languages - spHistoryItem->historyItemVector.expression = L'\u202d' + generatedExpression + L'\u202c'; + spHistoryItem->historyItemVector.expression = GetGeneratedExpression(*tokens); spHistoryItem->historyItemVector.result = wstring(result); - addedIndex = AddItem(spHistoryItem); - - return addedIndex; + return AddItem(spHistoryItem); } unsigned int CalculatorHistory::AddItem(_In_ shared_ptr const& spHistoryItem) @@ -66,19 +58,18 @@ unsigned int CalculatorHistory::AddItem(_In_ shared_ptr const& spHi } m_historyItems.push_back(spHistoryItem); - unsigned int lastIndex = static_cast(m_historyItems.size() - 1); - return lastIndex; + return static_cast(m_historyItems.size() - 1); } bool CalculatorHistory::RemoveItem(unsigned int uIdx) { - if (uIdx > m_historyItems.size() - 1) + if (uIdx < m_historyItems.size()) { - return false; + m_historyItems.erase(m_historyItems.begin() + uIdx); + return true; } - m_historyItems.erase(m_historyItems.begin() + uIdx); - return true; + return false; } vector> const& CalculatorHistory::GetHistory() diff --git a/src/CalcManager/CalculatorHistory.h b/src/CalcManager/CalculatorHistory.h index e8c874a0..2c849906 100644 --- a/src/CalcManager/CalculatorHistory.h +++ b/src/CalcManager/CalculatorHistory.h @@ -7,12 +7,6 @@ namespace CalculationManager { - enum CALCULATOR_MODE - { - CM_STD = 0, - CM_SCI, - }; - struct HISTORYITEMVECTOR { std::shared_ptr>> spTokens; diff --git a/src/CalcManager/CalculatorManager.cpp b/src/CalcManager/CalculatorManager.cpp index 8d6f1e51..a5e2f18c 100644 --- a/src/CalcManager/CalculatorManager.cpp +++ b/src/CalcManager/CalculatorManager.cpp @@ -10,16 +10,11 @@ using namespace std; using namespace CalcEngine; static constexpr size_t MAX_HISTORY_ITEMS = 20; -static constexpr size_t SERIALIZED_NUMBER_MINSIZE = 3; #ifndef _MSC_VER #define __pragma(x) #endif -// Converts Memory Command enum value to unsigned char, -// while ignoring Warning C4309: 'conversion' : truncation of constant value -#define MEMORY_COMMAND_TO_UNSIGNED_CHAR(c) __pragma(warning(push)) __pragma(warning(disable : 4309)) static_cast(c) __pragma(warning(pop)) - namespace CalculationManager { CalculatorManager::CalculatorManager(_In_ ICalcDisplay* displayCallback, _In_ IResourceProvider* resourceProvider) @@ -30,9 +25,9 @@ namespace CalculationManager , m_persistedPrimaryValue() , m_isExponentialFormat(false) , m_currentDegreeMode(Command::CommandNULL) - , m_savedDegreeMode(Command::CommandDEG) , m_pStdHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) , m_pSciHistory(new CalculatorHistory(MAX_HISTORY_ITEMS)) + , m_pHistory(nullptr) { CCalcEngine::InitialOneTimeOnlySetup(*m_resourceProvider); } @@ -131,7 +126,6 @@ namespace CalculationManager /// void CalculatorManager::Reset(bool clearMemory /* = true*/) { - m_savedCommands.clear(); SetStandardMode(); if (m_scientificCalculatorEngine) @@ -238,13 +232,6 @@ namespace CalculationManager m_currentCalculatorEngine->ProcessCommand(static_cast(command)); } - m_savedCommands.clear(); // Clear the previous command history - - if (command != Command::CommandEQU && command != Command::CommandCLEAR) - { - m_savedCommands.push_back(MapCommandForSerialize(command)); - } - m_savedDegreeMode = m_currentDegreeMode; InputChanged(); return; } @@ -254,11 +241,6 @@ namespace CalculationManager m_currentDegreeMode = command; } - if (command != Command::CommandFE) - { - m_savedCommands.push_back(MapCommandForSerialize(command)); // Save the commands in the m_savedCommands - } - switch (command) { case Command::CommandASIN: @@ -324,37 +306,6 @@ namespace CalculationManager InputChanged(); } - /// - /// Convert Command to unsigned char. - /// Since some Commands are higher than 255, they are saved after subtracting 255 - /// The smallest Command is CommandSIGN = 80, thus, subtracted value does not overlap with other values. - /// - /// Enum Command - unsigned char CalculatorManager::MapCommandForSerialize(Command command) - { - unsigned int commandToSave = static_cast(command); - if (commandToSave > UCHAR_MAX) - { - commandToSave -= UCHAR_MAX; - } - return static_cast(commandToSave); - } - - /// - /// Convert Command to unsigned int - /// The command that is smaller than 80, CommandSIGN, can be converted back to original value by adding 255. - /// - /// unsigned char value represent the saved command - unsigned int CalculatorManager::MapCommandForDeSerialize(unsigned char command) - { - unsigned int commandToLoad = command; - if (command < static_cast(Command::CommandSIGN)) - { - commandToLoad += UCHAR_MAX; - } - return commandToLoad; - } - /// /// Load the persisted value that is saved in memory of CalcEngine /// @@ -371,8 +322,6 @@ namespace CalculationManager /// void CalculatorManager::MemorizeNumber() { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizeNumber)); - if (m_currentCalculatorEngine->FInErrorState()) { return; @@ -400,8 +349,6 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberLoad(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberLoad, indexOfMemory); - if (m_currentCalculatorEngine->FInErrorState()) { return; @@ -420,8 +367,6 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberAdd(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberAdd, indexOfMemory); - if (m_currentCalculatorEngine->FInErrorState()) { return; @@ -448,7 +393,6 @@ namespace CalculationManager { if (indexOfMemory < m_memorizedNumbers.size()) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberClear, indexOfMemory); m_memorizedNumbers.erase(m_memorizedNumbers.begin() + indexOfMemory); } } @@ -461,8 +405,6 @@ namespace CalculationManager /// Index of the target memory void CalculatorManager::MemorizedNumberSubtract(_In_ unsigned int indexOfMemory) { - SaveMemoryCommand(MemoryCommand::MemorizedNumberSubtract, indexOfMemory); - if (m_currentCalculatorEngine->FInErrorState()) { return; @@ -494,7 +436,6 @@ namespace CalculationManager /// void CalculatorManager::MemorizedNumberClearAll() { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(MemoryCommand::MemorizedNumberClearAll)); m_memorizedNumbers.clear(); m_currentCalculatorEngine->ProcessCommand(IDC_MCLEAR); @@ -536,24 +477,14 @@ namespace CalculationManager } } - void CalculatorManager::SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory) - { - m_savedCommands.push_back(MEMORY_COMMAND_TO_UNSIGNED_CHAR(command)); - if (indexOfMemory > UCHAR_MAX) - { - throw invalid_argument("Unexpected value. IndexOfMemory is bigger than the biggest unsigned char"); - } - m_savedCommands.push_back(static_cast(indexOfMemory)); - } - vector> const& CalculatorManager::GetHistoryItems() { return m_pHistory->GetHistory(); } - vector> const& CalculatorManager::GetHistoryItems(_In_ CALCULATOR_MODE mode) + vector> const& CalculatorManager::GetHistoryItems(_In_ CalculatorMode mode) { - return (mode == CM_STD) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory(); + return (mode == CalculatorMode::Standard) ? m_pStdHistory->GetHistory() : m_pSciHistory->GetHistory(); } shared_ptr const& CalculatorManager::GetHistoryItem(_In_ unsigned int uIdx) @@ -576,20 +507,20 @@ namespace CalculationManager m_pHistory->ClearHistory(); } - void CalculatorManager::SetRadix(RADIX_TYPE iRadixType) + void CalculatorManager::SetRadix(RadixType iRadixType) { switch (iRadixType) { - case RADIX_TYPE::HEX_RADIX: + case RadixType::Hex: m_currentCalculatorEngine->ProcessCommand(IDC_HEX); break; - case RADIX_TYPE::DEC_RADIX: + case RadixType::Decimal: m_currentCalculatorEngine->ProcessCommand(IDC_DEC); break; - case RADIX_TYPE::OCT_RADIX: + case RadixType::Octal: m_currentCalculatorEngine->ProcessCommand(IDC_OCT); break; - case RADIX_TYPE::BIN_RADIX: + case RadixType::Binary: m_currentCalculatorEngine->ProcessCommand(IDC_BIN); break; default: @@ -603,7 +534,7 @@ namespace CalculationManager vector resultVector; for (auto const& memoryItem : m_memorizedNumbers) { - int radix = m_currentCalculatorEngine->GetCurrentRadix(); + auto radix = m_currentCalculatorEngine->GetCurrentRadix(); wstring stringValue = m_currentCalculatorEngine->GetStringForDisplay(memoryItem, radix); if (!stringValue.empty()) @@ -623,30 +554,6 @@ namespace CalculationManager return m_currentDegreeMode; } - void CalculatorManager::SetHistory(_In_ CALCULATOR_MODE eMode, _In_ vector> const& history) - { - CalculatorHistory* pHistory = nullptr; - - switch (eMode) - { - case CM_STD: - pHistory = m_pStdHistory.get(); - break; - case CM_SCI: - pHistory = m_pSciHistory.get(); - break; - } - - if (pHistory) - { - pHistory->ClearHistory(); - for (auto const& historyItem : history) - { - pHistory->AddItem(historyItem); - } - } - } - wstring CalculatorManager::GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix) { return m_currentCalculatorEngine ? m_currentCalculatorEngine->GetCurrentResultForRadix(radix, precision, groupDigitsPerRadix) : L""; @@ -669,7 +576,7 @@ namespace CalculationManager bool CalculatorManager::IsEngineRecording() { - return m_currentCalculatorEngine->FInRecordingState() ? true : false; + return m_currentCalculatorEngine->FInRecordingState(); } bool CalculatorManager::IsInputEmpty() diff --git a/src/CalcManager/CalculatorManager.h b/src/CalcManager/CalculatorManager.h index dd30c16a..88a8457b 100644 --- a/src/CalcManager/CalculatorManager.h +++ b/src/CalcManager/CalculatorManager.h @@ -15,9 +15,8 @@ namespace CalculationManager enum class CalculatorMode { - StandardMode, - ScientificMode, - ProgrammerMode, + Standard = 0, + Scientific, }; enum class CalculatorPrecision @@ -45,6 +44,7 @@ namespace CalculationManager class CalculatorManager final : public ICalcDisplay { private: + static const unsigned int m_maximumMemorySize = 100; ICalcDisplay* const m_displayCallback; CCalcEngine* m_currentCalculatorEngine; std::unique_ptr m_scientificCalculatorEngine; @@ -55,21 +55,8 @@ namespace CalculationManager std::vector m_memorizedNumbers; CalcEngine::Rational m_persistedPrimaryValue; - bool m_isExponentialFormat; - - static const unsigned int m_maximumMemorySize = 100; - - // For persistence - std::vector m_savedCommands; - std::vector m_savedPrimaryValue; - std::vector m_currentSerializedMemory; Command m_currentDegreeMode; - Command m_savedDegreeMode; - unsigned char MapCommandForSerialize(Command command); - unsigned int MapCommandForDeSerialize(unsigned char command); - - void SaveMemoryCommand(_In_ MemoryCommand command, _In_ unsigned int indexOfMemory); void MemorizedNumberSelect(_In_ unsigned int); void MemorizedNumberChanged(_In_ unsigned int); @@ -113,11 +100,7 @@ namespace CalculationManager bool IsEngineRecording(); bool IsInputEmpty(); - const std::vector& GetSavedCommands() const - { - return m_savedCommands; - } - void SetRadix(RADIX_TYPE iRadixType); + void SetRadix(RadixType iRadixType); void SetMemorizedNumbersString(); std::wstring GetResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); void SetPrecision(int32_t precision); @@ -125,7 +108,7 @@ namespace CalculationManager wchar_t DecimalSeparator(); std::vector> const& GetHistoryItems(); - std::vector> const& GetHistoryItems(_In_ CalculationManager::CALCULATOR_MODE mode); + std::vector> const& GetHistoryItems(_In_ CalculatorMode mode); std::shared_ptr const& GetHistoryItem(_In_ unsigned int uIdx); bool RemoveHistoryItem(_In_ unsigned int uIdx); void ClearHistory(); @@ -134,7 +117,6 @@ namespace CalculationManager return m_pHistory->MaxHistorySize(); } CalculationManager::Command GetCurrentDegreeMode(); - void SetHistory(_In_ CALCULATOR_MODE eMode, _In_ std::vector> const& history); void SetInHistoryItemLoadMode(_In_ bool isHistoryItemLoadMode); }; } diff --git a/src/CalcManager/CalculatorVector.h b/src/CalcManager/CalculatorVector.h new file mode 100644 index 00000000..4a139c8e --- /dev/null +++ b/src/CalcManager/CalculatorVector.h @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include "winerror_cross_platform.h" +#include "Ratpack/CalcErr.h" +#include // for std::out_of_range +#include "sal_cross_platform.h" // for SAL + +template +class CalculatorVector +{ +public: + ResultCode GetAt(_In_opt_ unsigned int index, _Out_ TType* item) + { + try + { + *item = m_vector.at(index); + } + catch (const std::out_of_range& /*ex*/) + { + return E_BOUNDS; + } + return S_OK; + } + + ResultCode GetSize(_Out_ unsigned int* size) + { + *size = static_cast(m_vector.size()); + return S_OK; + } + + ResultCode SetAt(_In_ unsigned int index, _In_opt_ TType item) + { + try + { + m_vector[index] = item; + } + catch (const std::out_of_range& /*ex*/) + { + return E_BOUNDS; + } + return S_OK; + } + + ResultCode RemoveAt(_In_ unsigned int index) + { + if (index < m_vector.size()) + { + m_vector.erase(m_vector.begin() + index); + } + else + { + return E_BOUNDS; + } + return S_OK; + } + + ResultCode InsertAt(_In_ unsigned int index, _In_ TType item) + { + try + { + auto iter = m_vector.begin() + index; + m_vector.insert(iter, item); + } + catch (const std::bad_alloc& /*ex*/) + { + return E_OUTOFMEMORY; + } + return S_OK; + } + + ResultCode Truncate(_In_ unsigned int index) + { + if (index < m_vector.size()) + { + auto startIter = m_vector.begin() + index; + m_vector.erase(startIter, m_vector.end()); + } + else + { + return E_BOUNDS; + } + return S_OK; + } + + ResultCode Append(_In_opt_ TType item) + { + try + { + m_vector.push_back(item); + } + catch (const std::bad_alloc& /*ex*/) + { + return E_OUTOFMEMORY; + } + return S_OK; + } + + ResultCode RemoveAtEnd() + { + m_vector.erase(--(m_vector.end())); + return S_OK; + } + + ResultCode Clear() + { + m_vector.clear(); + return S_OK; + } + + ResultCode GetString(_Out_ std::wstring* expression) + { + unsigned int nTokens = 0; + ResultCode hr = this->GetSize(&nTokens); + if (SUCCEEDED(hr)) + { + + std::pair currentPair; + for (unsigned int i = 0; i < nTokens; i++) + { + hr = this->GetAt(i, ¤tPair); + if (SUCCEEDED(hr)) + { + expression->append(currentPair.first); + + if (i != (nTokens - 1)) + { + expression->append(L" "); + } + } + } + + std::wstring expressionSuffix{}; + hr = GetExpressionSuffix(&expressionSuffix); + if (SUCCEEDED(hr)) + { + expression->append(expressionSuffix); + } + } + + return hr; + } + + ResultCode GetExpressionSuffix(_Out_ std::wstring* suffix) + { + *suffix = L" ="; + return S_OK; + } + +private: + std::vector m_vector; +}; diff --git a/src/CalcManager/Command.h b/src/CalcManager/Command.h index 4331ddf7..ab095189 100644 --- a/src/CalcManager/Command.h +++ b/src/CalcManager/Command.h @@ -69,10 +69,6 @@ namespace CalculationManager CommandNULL = 0, - // No new command should not be added before CommandSign, 80 - // If it is needed, the following two functions need to be revised too. - // CalculatorManager::MapCommandForSerialize(Command command); - // CalculatorManager::MapCommandForDeSerialize(unsigned char command); CommandSIGN = 80, CommandCLEAR = 81, CommandCENTR = 82, @@ -172,7 +168,7 @@ namespace CalculationManager CommandCeil = 415, CommandROLC = 416, CommandRORC = 417, - CommandLogBaseX = 500, + CommandLogBaseY = 500, CommandNand = 501, CommandNor = 502, diff --git a/src/CalcManager/ExpressionCommand.cpp b/src/CalcManager/ExpressionCommand.cpp index 628805fb..d574c9da 100644 --- a/src/CalcManager/ExpressionCommand.cpp +++ b/src/CalcManager/ExpressionCommand.cpp @@ -277,7 +277,7 @@ wstring COpndCommand::GetString(uint32_t radix, int32_t precision) { if (m_fInitialized) { - return m_value.ToString(radix, eNUMOBJ_FMT::FMT_FLOAT, precision); + return m_value.ToString(radix, NumberFormat::Float, precision); } return wstring{}; diff --git a/src/CalcManager/Header Files/CCommand.h b/src/CalcManager/Header Files/CCommand.h index f6d74d25..440c6283 100644 --- a/src/CalcManager/Header Files/CCommand.h +++ b/src/CalcManager/Header Files/CCommand.h @@ -166,7 +166,7 @@ #define IDC_LASTCONTROL IDC_CEIL #define IDC_BINARYEXTENDEDFIRST 500 -#define IDC_LOGBASEX 500 // logx(y) +#define IDC_LOGBASEY 500 // logy(x) #define IDC_NAND 501 // Nand #define IDC_NOR 502 // Nor diff --git a/src/CalcManager/Header Files/CalcEngine.h b/src/CalcManager/Header Files/CalcEngine.h index 26b80515..397b9cea 100644 --- a/src/CalcManager/Header Files/CalcEngine.h +++ b/src/CalcManager/Header Files/CalcEngine.h @@ -31,14 +31,13 @@ // The real exports follows later // This is expected to be in same order as IDM_QWORD, IDM_DWORD etc. -enum eNUM_WIDTH +enum class NUM_WIDTH { QWORD_WIDTH, // Number width of 64 bits mode (default) DWORD_WIDTH, // Number width of 32 bits mode WORD_WIDTH, // Number width of 16 bits mode BYTE_WIDTH // Number width of 16 bits mode }; -typedef enum eNUM_WIDTH NUM_WIDTH; static constexpr size_t NUM_WIDTH_LENGTH = 4; namespace CalculationManager @@ -78,7 +77,7 @@ public: } void SettingsChanged(); bool IsCurrentTooBigForTrig(); - int GetCurrentRadix(); + uint32_t GetCurrentRadix(); std::wstring GetCurrentResultForRadix(uint32_t radix, int32_t precision, bool groupDigitsPerRadix); void ChangePrecision(int32_t precision) { @@ -106,7 +105,7 @@ public: { return GetString(IdStrFromCmdId(nOpCode)); } - static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, ANGLE_TYPE angletype); + static std::wstring_view OpCodeToUnaryString(int nOpCode, bool fInv, AngleType angletype); static std::wstring_view OpCodeToBinaryString(int nOpCode, bool isIntegerMode); private: @@ -117,11 +116,11 @@ private: int m_nOpCode; /* ID value of operation. */ int m_nPrevOpCode; // opcode which computed the number in m_currentVal. 0 if it is already bracketed or plain number or // if it hasn't yet been computed - bool m_bChangeOp; /* Flag for changing operation. */ + bool m_bChangeOp; // Flag for changing operation bool m_bRecord; // Global mode: recording or displaying bool m_bSetCalcState; // Flag for setting the engine result state CalcEngine::CalcInput m_input; // Global calc input object for decimal strings - eNUMOBJ_FMT m_nFE; /* Scientific notation conversion flag. */ + NumberFormat m_nFE; // Scientific notation conversion flag CalcEngine::Rational m_maxTrigonometricNum; std::unique_ptr m_memoryValue; // Current memory value. @@ -148,7 +147,7 @@ private: std::array m_nPrecOp; /* Holding array for precedence operations. */ size_t m_precedenceOpCount; /* Current number of precedence ops in holding. */ int m_nLastCom; // Last command entered. - ANGLE_TYPE m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad + AngleType m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad NUM_WIDTH m_numwidth; // one of qword, dword, word or byte mode. int32_t m_dwWordBitWidth; // # of bits in currently selected word size @@ -179,15 +178,17 @@ private: CalcEngine::Rational TruncateNumForIntMath(CalcEngine::Rational const& rat); CalcEngine::Rational SciCalcFunctions(CalcEngine::Rational const& rat, uint32_t op); CalcEngine::Rational DoOperation(int operation, CalcEngine::Rational const& lhs, CalcEngine::Rational const& rhs); - void SetRadixTypeAndNumWidth(RADIX_TYPE radixtype, NUM_WIDTH numwidth); + void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth); int32_t DwWordBitWidthFromeNumWidth(NUM_WIDTH numwidth); - uint32_t NRadixFromRadixType(RADIX_TYPE radixtype); + uint32_t NRadixFromRadixType(RadixType radixtype); double GenerateRandomNumber(); bool TryToggleBit(CalcEngine::Rational& rat, uint32_t wbitno); void CheckAndAddLastBinOpToHistory(bool addToHistory = true); void InitChopNumbers(); + CalcEngine::Rational GetChopNumber() const; + std::wstring GetMaxDecimalValueString() const; static void LoadEngineStrings(CalculationManager::IResourceProvider& resourceProvider); static int IdStrFromCmdId(int id) diff --git a/src/CalcManager/Header Files/EngineStrings.h b/src/CalcManager/Header Files/EngineStrings.h index 958c828b..7e093237 100644 --- a/src/CalcManager/Header Files/EngineStrings.h +++ b/src/CalcManager/Header Files/EngineStrings.h @@ -196,7 +196,7 @@ inline constexpr auto SIDS_ACSCH = L"InverseCsch"; inline constexpr auto SIDS_COTH = L"Coth"; inline constexpr auto SIDS_ACOTH = L"InverseCoth"; inline constexpr auto SIDS_TWOPOWX = L"TwoPowX"; -inline constexpr auto SIDS_LOGBASEX = L"LogBaseX"; +inline constexpr auto SIDS_LOGBASEY = L"LogBaseY"; inline constexpr auto SIDS_ABS = L"Abs"; inline constexpr auto SIDS_FLOOR = L"Floor"; inline constexpr auto SIDS_CEIL = L"Ceil"; @@ -352,7 +352,7 @@ inline constexpr std::array g_sids = SIDS_COTH, SIDS_ACOTH, SIDS_TWOPOWX, - SIDS_LOGBASEX, + SIDS_LOGBASEY, SIDS_ABS, SIDS_FLOOR, SIDS_CEIL, diff --git a/src/CalcManager/Header Files/History.h b/src/CalcManager/Header Files/History.h index 66bac67f..f88f61c3 100644 --- a/src/CalcManager/Header Files/History.h +++ b/src/CalcManager/Header Files/History.h @@ -23,7 +23,7 @@ public: void RemoveLastOpndFromHistory(); void AddBinOpToHistory(int nOpCode, bool isIntgerMode, bool fNoRepetition = true); void ChangeLastBinOp(int nOpCode, bool fPrecInvToHigher, bool isIntgerMode); - void AddUnaryOpToHistory(int nOpCode, bool fInv, ANGLE_TYPE angletype); + void AddUnaryOpToHistory(int nOpCode, bool fInv, AngleType angletype); void AddOpenBraceToHistory(); void AddCloseBraceToHistory(); void PushLastOpndStart(int ichOpndStart = -1); diff --git a/src/CalcManager/Header Files/RadixType.h b/src/CalcManager/Header Files/RadixType.h index 380d6afe..2ae0f90a 100644 --- a/src/CalcManager/Header Files/RadixType.h +++ b/src/CalcManager/Header Files/RadixType.h @@ -4,11 +4,10 @@ #pragma once // This is expected to be in same order as IDM_HEX, IDM_DEC, IDM_OCT, IDM_BIN -enum eRADIX_TYPE +enum class RadixType { - HEX_RADIX, - DEC_RADIX, - OCT_RADIX, - BIN_RADIX + Hex, + Decimal, + Octal, + Binary }; -typedef enum eRADIX_TYPE RADIX_TYPE; diff --git a/src/CalcManager/Header Files/Rational.h b/src/CalcManager/Header Files/Rational.h index 8ded3c27..25df92bd 100644 --- a/src/CalcManager/Header Files/Rational.h +++ b/src/CalcManager/Header Files/Rational.h @@ -64,7 +64,7 @@ namespace CalcEngine friend bool operator<=(Rational const& lhs, Rational const& rhs); friend bool operator>=(Rational const& lhs, Rational const& rhs); - std::wstring ToString(uint32_t radix, NUMOBJ_FMT format, int32_t precision) const; + std::wstring ToString(uint32_t radix, NumberFormat format, int32_t precision) const; uint64_t ToUInt64_t() const; private: diff --git a/src/CalcManager/Header Files/RationalMath.h b/src/CalcManager/Header Files/RationalMath.h index 59500573..f3ba56ec 100644 --- a/src/CalcManager/Header Files/RationalMath.h +++ b/src/CalcManager/Header Files/RationalMath.h @@ -22,12 +22,12 @@ namespace CalcEngine::RationalMath Rational Invert(Rational const& rat); Rational Abs(Rational const& rat); - Rational Sin(Rational const& rat, ANGLE_TYPE angletype); - Rational Cos(Rational const& rat, ANGLE_TYPE angletype); - Rational Tan(Rational const& rat, ANGLE_TYPE angletype); - Rational ASin(Rational const& rat, ANGLE_TYPE angletype); - Rational ACos(Rational const& rat, ANGLE_TYPE angletype); - Rational ATan(Rational const& rat, ANGLE_TYPE angletype); + Rational Sin(Rational const& rat, AngleType angletype); + Rational Cos(Rational const& rat, AngleType angletype); + Rational Tan(Rational const& rat, AngleType angletype); + Rational ASin(Rational const& rat, AngleType angletype); + Rational ACos(Rational const& rat, AngleType angletype); + Rational ATan(Rational const& rat, AngleType angletype); Rational Sinh(Rational const& rat); Rational Cosh(Rational const& rat); diff --git a/src/CalcManager/NumberFormattingUtils.cpp b/src/CalcManager/NumberFormattingUtils.cpp index ead9013f..63563972 100644 --- a/src/CalcManager/NumberFormattingUtils.cpp +++ b/src/CalcManager/NumberFormattingUtils.cpp @@ -50,15 +50,15 @@ namespace CalcManager::NumberFormattingUtils /// the number unsigned int GetNumberDigitsWholeNumberPart(double value) { - return value == 0 ? 1 : (1 + (int)log10(abs(value))); + return value == 0 ? 1u : static_cast(1 + max(0.0, 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) + /// unsigned int number of significant digits to round to + wstring RoundSignificantDigits(double num, unsigned int numSignificant) { wstringstream out(wstringstream::out); out << fixed; diff --git a/src/CalcManager/NumberFormattingUtils.h b/src/CalcManager/NumberFormattingUtils.h index ab337eed..2f13ebfc 100644 --- a/src/CalcManager/NumberFormattingUtils.h +++ b/src/CalcManager/NumberFormattingUtils.h @@ -10,6 +10,6 @@ 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 RoundSignificantDigits(double value, unsigned int numberSignificantDigits); std::wstring ToScientificNumber(double number); } diff --git a/src/CalcManager/Ratpack/CMakeLists.txt b/src/CalcManager/Ratpack/CMakeLists.txt deleted file mode 100644 index 3fbd7ae3..00000000 --- a/src/CalcManager/Ratpack/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -target_sources(CalcManager PRIVATE - basex.cpp - conv.cpp - exp.cpp - fact.cpp - itrans.cpp - itransh.cpp - logic.cpp - num.cpp - rat.cpp - support.cpp - trans.cpp - transh.cpp -) diff --git a/src/CalcManager/Ratpack/conv.cpp b/src/CalcManager/Ratpack/conv.cpp index ab7aba05..4f7609de 100644 --- a/src/CalcManager/Ratpack/conv.cpp +++ b/src/CalcManager/Ratpack/conv.cpp @@ -303,17 +303,13 @@ PRAT numtorat(_In_ PNUMBER pin, uint32_t radix) PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision) { - uint32_t bitmask; - uint32_t cdigits; - MANTTYPE* ptr; - PNUMBER sum = i32tonum(0, radix); PNUMBER powofnRadix = i32tonum(BASEX, radix); // A large penalty is paid for conversion of digits no one will see anyway. // limit the digits to the minimum of the existing precision or the // requested precision. - cdigits = precision + 1; + uint32_t cdigits = precision + 1; if (cdigits > (uint32_t)a->cdigit) { cdigits = (uint32_t)a->cdigit; @@ -323,10 +319,10 @@ PNUMBER nRadixxtonum(_In_ PNUMBER a, uint32_t radix, int32_t precision) numpowi32(&powofnRadix, a->exp + (a->cdigit - cdigits), radix, precision); // Loop over all the relative digits from MSD to LSD - for (ptr = &(a->mant[a->cdigit - 1]); cdigits > 0; ptr--, cdigits--) + for (MANTTYPE* ptr = &(a->mant[a->cdigit - 1]); cdigits > 0; ptr--, cdigits--) { // Loop over all the bits from MSB to LSB - for (bitmask = BASEX / 2; bitmask > 0; bitmask /= 2) + for (uint32_t bitmask = BASEX / 2; bitmask > 0; bitmask /= 2) { addnum(&sum, sum, radix); if (*ptr & bitmask) @@ -368,9 +364,7 @@ PNUMBER numtonRadixx(_In_ PNUMBER a, uint32_t radix) ptrdigit += a->cdigit - 1; PNUMBER thisdigit = nullptr; // thisdigit holds the current digit of a - // being summed into result. - int32_t idigit; // idigit is the iterate of digits in a. - for (idigit = 0; idigit < a->cdigit; idigit++) + for (int32_t idigit = 0; idigit < a->cdigit; idigit++) { mulnumx(&pnumret, num_radix); // WARNING: @@ -628,11 +622,10 @@ PNUMBER StringToNumber(wstring_view numberString, uint32_t radix, int32_t precis MANTTYPE* pmant = pnumret->mant + numberString.length() - 1; uint8_t state = START; // state is the state of the input state machine. - wchar_t curChar; for (const auto& c : numberString) { // If the character is the decimal separator, use L'.' for the purposes of the state machine. - curChar = (c == g_decimalSeparator ? L'.' : c); + wchar_t curChar = (c == g_decimalSeparator ? L'.' : c); // Switch states based on the character we encountered switch (curChar) @@ -1032,13 +1025,10 @@ int32_t numtoi32(_In_ PNUMBER pnum, uint32_t radix) bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting) { - MANTTYPE* pmant; - int32_t cdigits; bool fstrip = false; - // point pmant to the LeastCalculatedDigit - pmant = pnum->mant; - cdigits = pnum->cdigit; + MANTTYPE* pmant = pnum->mant; + int32_t cdigits = pnum->cdigit; // point pmant to the LSD if (cdigits > starting) { @@ -1073,27 +1063,27 @@ bool stripzeroesnum(_Inout_ PNUMBER pnum, int32_t starting) // FUNCTION: NumberToString // // ARGUMENTS: number representation -// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or -// FMT_ENGINEERING +// fmt, one of NumberFormat::Float, NumberFormat::Scientific or +// NumberFormat::Engineering // integer radix and int32_t precision value // // RETURN: String representation of number. // -// DESCRIPTION: Converts a number to it's string +// DESCRIPTION: Converts a number to its string // representation. // //----------------------------------------------------------------------------- -wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision) +wstring NumberToString(_Inout_ PNUMBER& pnum, NumberFormat format, uint32_t radix, int32_t precision) { stripzeroesnum(pnum, precision + 2); int32_t length = pnum->cdigit; int32_t exponent = pnum->exp + length; // Actual number of digits to the left of decimal - int32_t oldFormat = format; - if (exponent > precision && format == FMT_FLOAT) + NumberFormat oldFormat = format; + if (exponent > precision && format == NumberFormat::Float) { // Force scientific mode to prevent user from assuming 33rd digit is exact. - format = FMT_SCIENTIFIC; + format = NumberFormat::Scientific; } // Make length small enough to fit in pret. @@ -1113,7 +1103,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ divnum(&round, num_two, radix, precision); // Make round number exponent one below the LSD for the number. - if (exponent > 0 || format == FMT_FLOAT) + if (exponent > 0 || format == NumberFormat::Float) { round->exp = pnum->exp + pnum->cdigit - round->cdigit - precision; } @@ -1126,7 +1116,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ round->sign = pnum->sign; } - if (format == FMT_FLOAT) + if (format == NumberFormat::Float) { // Figure out if the exponent will fill more space than the non-exponent field. if ((length - exponent > precision) || (exponent > precision + 3)) @@ -1140,7 +1130,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ { // Case where too many zeros are to the right or left of the // decimal pt. And we are forced to switch to scientific form. - format = FMT_SCIENTIFIC; + format = NumberFormat::Scientific; } } else if (length + abs(exponent) < precision && round) @@ -1173,13 +1163,13 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ int32_t eout = exponent - 1; // Displayed exponent. MANTTYPE* pmant = pnum->mant + pnum->cdigit - 1; // Case where too many digits are to the left of the decimal or - // FMT_SCIENTIFIC or FMT_ENGINEERING was specified. - if ((format == FMT_SCIENTIFIC) || (format == FMT_ENGINEERING)) + // NumberFormat::Scientific or NumberFormat::Engineering was specified. + if ((format == NumberFormat::Scientific) || (format == NumberFormat::Engineering)) { useSciForm = true; if (eout != 0) { - if (format == FMT_ENGINEERING) + if (format == NumberFormat::Engineering) { exponent = (eout % 3); eout -= exponent; @@ -1280,7 +1270,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ // ARGUMENTS: // PRAT *representation of a number. // i32 representation of base to dump to screen. -// fmt, one of FMT_FLOAT FMT_SCIENTIFIC or FMT_ENGINEERING +// fmt, one of NumberFormat::Float, NumberFormat::Scientific, or NumberFormat::Engineering // precision uint32_t // // RETURN: string @@ -1293,7 +1283,7 @@ wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_ // why a pointer to the rational is passed in. // //----------------------------------------------------------------------------- -wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision) +wstring RatToString(_Inout_ PRAT& prat, NumberFormat format, uint32_t radix, int32_t precision) { PNUMBER p = RatToNumber(prat, radix, precision); diff --git a/src/CalcManager/Ratpack/exp.cpp b/src/CalcManager/Ratpack/exp.cpp index 23257f73..b4020d45 100644 --- a/src/CalcManager/Ratpack/exp.cpp +++ b/src/CalcManager/Ratpack/exp.cpp @@ -63,7 +63,6 @@ void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) { PRAT pwr = nullptr; PRAT pint = nullptr; - int32_t intpwr; if (rat_gt(*px, rat_max_exp, precision) || rat_lt(*px, rat_min_exp, precision)) { @@ -76,7 +75,7 @@ void exprat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) intrat(&pint, radix, precision); - intpwr = rattoi32(pint, radix, precision); + const int32_t intpwr = rattoi32(pint, radix, precision); ratpowi32(&pwr, intpwr, precision); subrat(px, pint, precision); @@ -153,7 +152,6 @@ void _lograt(PRAT* px, int32_t precision) void lograt(_Inout_ PRAT* px, int32_t precision) { - bool fneglog; PRAT pwr = nullptr; // pwr is the large scaling factor. PRAT offset = nullptr; // offset is the incremental scaling factor. @@ -164,12 +162,10 @@ void lograt(_Inout_ PRAT* px, int32_t precision) } // Get number > 1, for scaling - fneglog = rat_lt(*px, rat_one, precision); + bool fneglog = rat_lt(*px, rat_one, precision); if (fneglog) { - // WARNING: This is equivalent to doing *px = 1 / *px - PNUMBER pnumtemp = nullptr; - pnumtemp = (*px)->pp; + PNUMBER pnumtemp = (*px)->pp; (*px)->pp = (*px)->pq; (*px)->pq = pnumtemp; } @@ -178,10 +174,7 @@ void lograt(_Inout_ PRAT* px, int32_t precision) // log(x*2^(BASEXPWR*k)) = BASEXPWR*k*log(2)+log(x) if (LOGRAT2(*px) > 1) { - // Take advantage of px's base BASEX to scale quickly down to - // a reasonable range. - int32_t intpwr; - intpwr = LOGRAT2(*px) - 1; + const int32_t intpwr = LOGRAT2(*px) - 1; (*px)->pq->exp += intpwr; pwr = i32torat(intpwr * BASEXPWR); mulrat(&pwr, ln_two, precision); @@ -448,10 +441,9 @@ void powratcomp(_Inout_ PRAT* px, _In_ PRAT y, uint32_t radix, int32_t precision { // If power is an integer let ratpowi32 deal with it. PRAT iy = nullptr; - int32_t inty; DUPRAT(iy, y); subrat(&iy, podd, precision); - inty = rattoi32(iy, radix, precision); + int32_t inty = rattoi32(iy, radix, precision); PRAT plnx = nullptr; DUPRAT(plnx, *px); diff --git a/src/CalcManager/Ratpack/fact.cpp b/src/CalcManager/Ratpack/fact.cpp index c026e536..f0905e4f 100644 --- a/src/CalcManager/Ratpack/fact.cpp +++ b/src/CalcManager/Ratpack/fact.cpp @@ -67,13 +67,9 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision) PRAT sum = nullptr; PRAT err = nullptr; PRAT mpy = nullptr; - PRAT ratprec = nullptr; - PRAT ratRadix = nullptr; - int32_t oldprec; // Set up constants and initial conditions - oldprec = precision; - ratprec = i32torat(oldprec); + PRAT ratprec = i32torat(precision); // Find the best 'A' for convergence to the required precision. a = i32torat(radix); @@ -102,7 +98,7 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision) exprat(&tmp, radix, precision); mulrat(&term, tmp, precision); lograt(&term, precision); - ratRadix = i32torat(radix); + const auto ratRadix = i32torat(radix); DUPRAT(tmp, ratRadix); lograt(&tmp, precision); subrat(&term, tmp, precision); @@ -173,7 +169,6 @@ void _gamma(PRAT* pn, uint32_t radix, int32_t precision) mulrat(&sum, mpy, precision); // And cleanup - precision = oldprec; destroyrat(ratprec); destroyrat(err); destroyrat(term); diff --git a/src/CalcManager/Ratpack/itrans.cpp b/src/CalcManager/Ratpack/itrans.cpp index 9bb94358..75e74785 100644 --- a/src/CalcManager/Ratpack/itrans.cpp +++ b/src/CalcManager/Ratpack/itrans.cpp @@ -17,17 +17,17 @@ //----------------------------------------------------------------------------- #include "ratpak.h" -void ascalerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, int32_t precision) +void ascalerat(_Inout_ PRAT* pa, AngleType angletype, int32_t precision) { switch (angletype) { - case ANGLE_RAD: + case AngleType::Radians: break; - case ANGLE_DEG: + case AngleType::Degrees: divrat(pa, two_pi, precision); mulrat(pa, rat_360, precision); break; - case ANGLE_GRAD: + case AngleType::Gradians: divrat(pa, two_pi, precision); mulrat(pa, rat_400, precision); break; @@ -76,7 +76,7 @@ void _asinrat(PRAT* px, int32_t precision) DESTROYTAYLOR(); } -void asinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void asinanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { asinrat(pa, radix, precision); @@ -164,7 +164,7 @@ void asinrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void acosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void acosanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { acosrat(pa, radix, precision); @@ -249,7 +249,7 @@ void acosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) // //----------------------------------------------------------------------------- -void atananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void atananglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { atanrat(pa, radix, precision); diff --git a/src/CalcManager/Ratpack/logic.cpp b/src/CalcManager/Ratpack/logic.cpp index c9094f33..197bda90 100644 --- a/src/CalcManager/Ratpack/logic.cpp +++ b/src/CalcManager/Ratpack/logic.cpp @@ -21,7 +21,6 @@ void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { PRAT pwr = nullptr; - int32_t intb; intrat(pa, radix, precision); if (!zernum((*pa)->pp)) @@ -32,7 +31,7 @@ void lshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) // Don't attempt lsh of anything big throw(CALC_E_DOMAIN); } - intb = rattoi32(b, radix, precision); + const int32_t intb = rattoi32(b, radix, precision); DUPRAT(pwr, rat_two); ratpowi32(&pwr, intb, precision); mulrat(pa, pwr, precision); @@ -44,7 +43,6 @@ void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) { PRAT pwr = nullptr; - int32_t intb; intrat(pa, radix, precision); if (!zernum((*pa)->pp)) @@ -55,7 +53,7 @@ void rshrat(_Inout_ PRAT* pa, _In_ PRAT b, uint32_t radix, int32_t precision) // Don't attempt rsh of anything big and negative. throw(CALC_E_DOMAIN); } - intb = rattoi32(b, radix, precision); + const int32_t intb = rattoi32(b, radix, precision); DUPRAT(pwr, rat_two); ratpowi32(&pwr, intb, precision); divrat(pa, pwr, precision); diff --git a/src/CalcManager/Ratpack/num.cpp b/src/CalcManager/Ratpack/num.cpp index c4561052..6055e41d 100644 --- a/src/CalcManager/Ratpack/num.cpp +++ b/src/CalcManager/Ratpack/num.cpp @@ -67,8 +67,7 @@ void _addnum(PNUMBER* pa, PNUMBER b, uint32_t radix) MANTTYPE* pcha; // pcha is a pointer to the mantissa of a. MANTTYPE* pchb; // pchb is a pointer to the mantissa of b. MANTTYPE* pchc; // pchc is a pointer to the mantissa of c. - int32_t cdigits; // cdigits is the max count of the digits results - // used as a counter. + int32_t cdigits; // cdigits is the max count of the digits results used as a counter. int32_t mexp; // mexp is the exponent of the result. MANTTYPE da; // da is a single 'digit' after possible padding. MANTTYPE db; // db is a single 'digit' after possible padding. @@ -558,48 +557,34 @@ bool equnum(_In_ PNUMBER a, _In_ PNUMBER b) bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b) { - int32_t diff; - MANTTYPE* pa; - MANTTYPE* pb; - int32_t cdigits; - int32_t ccdigits; - MANTTYPE da; - MANTTYPE db; - - diff = (a->cdigit + a->exp) - (b->cdigit + b->exp); + int32_t diff = (a->cdigit + a->exp) - (b->cdigit + b->exp); if (diff < 0) { // The exponent of a is less than b return true; } - else + if (diff > 0) { - if (diff > 0) + return false; + } + MANTTYPE* pa = a->mant; + MANTTYPE* pb = b->mant; + pa += a->cdigit - 1; + pb += b->cdigit - 1; + int32_t cdigits = max(a->cdigit, b->cdigit); + int32_t ccdigits = cdigits; + for (; cdigits > 0; cdigits--) + { + MANTTYPE da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0); + MANTTYPE db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0); + diff = da - db; + if (diff) { - return false; - } - else - { - pa = a->mant; - pb = b->mant; - pa += a->cdigit - 1; - pb += b->cdigit - 1; - cdigits = max(a->cdigit, b->cdigit); - ccdigits = cdigits; - for (; cdigits > 0; cdigits--) - { - da = ((cdigits > (ccdigits - a->cdigit)) ? *pa-- : 0); - db = ((cdigits > (ccdigits - b->cdigit)) ? *pb-- : 0); - diff = da - db; - if (diff) - { - return (diff < 0); - } - } - // In this case, they are equal. - return false; + return (diff < 0); } } + // In this case, they are equal. + return false; } //---------------------------------------------------------------------------- diff --git a/src/CalcManager/Ratpack/ratpak.h b/src/CalcManager/Ratpack/ratpak.h index 18b2d024..0b3771f3 100644 --- a/src/CalcManager/Ratpack/ratpak.h +++ b/src/CalcManager/Ratpack/ratpak.h @@ -31,25 +31,20 @@ static constexpr uint32_t BASEX = 0x80000000; // Internal radix used in calculat typedef uint32_t MANTTYPE; typedef uint64_t TWO_MANTTYPE; -enum eNUMOBJ_FMT +enum class NumberFormat { - FMT_FLOAT, // returns floating point, or exponential if number is too big - FMT_SCIENTIFIC, // always returns scientific notation - FMT_ENGINEERING // always returns engineering notation such that exponent is a multiple of 3 - + Float, // returns floating point, or exponential if number is too big + Scientific, // always returns scientific notation + Engineering // always returns engineering notation such that exponent is a multiple of 3 }; -enum eANGLE_TYPE +enum class AngleType { - ANGLE_DEG, // Calculate trig using 360 degrees per revolution - ANGLE_RAD, // Calculate trig using 2 pi radians per revolution - ANGLE_GRAD // Calculate trig using 400 gradients per revolution - + Degrees, // Calculate trig using 360 degrees per revolution + Radians, // Calculate trig using 2 pi radians per revolution + Gradians // Calculate trig using 400 gradians per revolution }; -typedef enum eNUMOBJ_FMT NUMOBJ_FMT; -typedef enum eANGLE_TYPE ANGLE_TYPE; - //----------------------------------------------------------------------------- // // NUMBER type is a representation of a generic sized generic radix number @@ -341,10 +336,10 @@ extern bool equnum(_In_ PNUMBER a, _In_ PNUMBER b); // returns true of a == b extern bool lessnum(_In_ PNUMBER a, _In_ PNUMBER b); // returns true of a < b extern bool zernum(_In_ PNUMBER a); // returns true of a == 0 extern bool zerrat(_In_ PRAT a); // returns true if a == 0/q -extern std::wstring NumberToString(_Inout_ PNUMBER& pnum, int format, uint32_t radix, int32_t precision); +extern std::wstring NumberToString(_Inout_ PNUMBER& pnum, NumberFormat format, uint32_t radix, int32_t precision); // returns a text representation of a PRAT -extern std::wstring RatToString(_Inout_ PRAT& prat, int format, uint32_t radix, int32_t precision); +extern std::wstring RatToString(_Inout_ PRAT& prat, NumberFormat format, uint32_t radix, int32_t precision); // converts a PRAT into a PNUMBER extern PNUMBER RatToNumber(_In_ PRAT prat, uint32_t radix, int32_t precision); // flattens a PRAT by converting it to a PNUMBER and back to a PRAT @@ -376,7 +371,7 @@ extern PRAT _createrat(void); // returns a new rat structure with the acos of x->p/x->q taking into account // angle type -extern void acosanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void acosanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); // returns a new rat structure with the acosh of x->p/x->q extern void acoshrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); @@ -386,7 +381,7 @@ extern void acosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the asin of x->p/x->q taking into account // angle type -extern void asinanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void asinanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); extern void asinhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the asinh of x->p/x->q @@ -396,7 +391,7 @@ extern void asinrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the atan of x->p/x->q taking into account // angle type -extern void atananglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void atananglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); // returns a new rat structure with the atanh of x->p/x->q extern void atanhrat(_Inout_ PRAT* px, int32_t precision); @@ -412,7 +407,7 @@ extern void cosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the cos of x->p/x->q taking into account // angle type -extern void cosanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void cosanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); // returns a new rat structure with the exp of x->p/x->q this should not be called explicitly. extern void _exprat(_Inout_ PRAT* px, int32_t precision); @@ -435,14 +430,14 @@ extern void sinrat(_Inout_ PRAT* px); // returns a new rat structure with the sin of x->p/x->q taking into account // angle type -extern void sinanglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void sinanglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); extern void tanhrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); extern void tanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision); // returns a new rat structure with the tan of x->p/x->q taking into account // angle type -extern void tananglerat(_Inout_ PRAT* px, ANGLE_TYPE angletype, uint32_t radix, int32_t precision); +extern void tananglerat(_Inout_ PRAT* px, AngleType angletype, uint32_t radix, int32_t precision); extern void _dupnum(_In_ PNUMBER dest, _In_ const NUMBER* const src); diff --git a/src/CalcManager/Ratpack/support.cpp b/src/CalcManager/Ratpack/support.cpp index 81c737f5..ef9e4106 100644 --- a/src/CalcManager/Ratpack/support.cpp +++ b/src/CalcManager/Ratpack/support.cpp @@ -596,15 +596,13 @@ void _dumprawrat(_In_ const wchar_t* varname, _In_ PRAT rat, wostream& out) void _dumprawnum(_In_ const wchar_t* varname, _In_ PNUMBER num, wostream& out) { - int i; - out << L"NUMBER " << varname << L" = {\n"; out << L"\t" << num->sign << L",\n"; out << L"\t" << num->cdigit << L",\n"; out << L"\t" << num->exp << L",\n"; out << L"\t{ "; - for (i = 0; i < num->cdigit; i++) + for (int i = 0; i < num->cdigit; i++) { out << L" " << num->mant[i] << L","; } @@ -681,10 +679,9 @@ void trimit(_Inout_ PRAT* px, int32_t precision) { if (!g_ftrueinfinite) { - int32_t trim; PNUMBER pp = (*px)->pp; PNUMBER pq = (*px)->pq; - trim = g_ratio * (min((pp->cdigit + pp->exp), (pq->cdigit + pq->exp)) - 1) - precision; + int32_t trim = g_ratio * (min((pp->cdigit + pp->exp), (pq->cdigit + pq->exp)) - 1) - precision; if (trim > g_ratio) { trim /= g_ratio; diff --git a/src/CalcManager/Ratpack/trans.cpp b/src/CalcManager/Ratpack/trans.cpp index e848155d..50055e22 100644 --- a/src/CalcManager/Ratpack/trans.cpp +++ b/src/CalcManager/Ratpack/trans.cpp @@ -16,17 +16,17 @@ #include "ratpak.h" -void scalerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void scalerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { switch (angletype) { - case ANGLE_RAD: + case AngleType::Radians: scale2pi(pa, radix, precision); break; - case ANGLE_DEG: + case AngleType::Degrees: scale(pa, rat_360, radix, precision); break; - case ANGLE_GRAD: + case AngleType::Gradians: scale(pa, rat_400, radix, precision); break; } @@ -98,13 +98,13 @@ void sinrat(PRAT* px, uint32_t radix, int32_t precision) _sinrat(px, precision); } -void sinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void sinanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { scalerat(pa, angletype, radix, precision); switch (angletype) { - case ANGLE_DEG: + case AngleType::Degrees: if (rat_gt(*pa, rat_180, precision)) { subrat(pa, rat_360, precision); @@ -112,7 +112,7 @@ void sinanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_180, precision); mulrat(pa, pi, precision); break; - case ANGLE_GRAD: + case AngleType::Gradians: if (rat_gt(*pa, rat_200, precision)) { subrat(pa, rat_400, precision); @@ -193,13 +193,13 @@ void cosrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) _cosrat(px, radix, precision); } -void cosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void cosanglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { scalerat(pa, angletype, radix, precision); switch (angletype) { - case ANGLE_DEG: + case AngleType::Degrees: if (rat_gt(*pa, rat_180, precision)) { PRAT ptmp = nullptr; @@ -211,7 +211,7 @@ void cosanglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_180, precision); mulrat(pa, pi, precision); break; - case ANGLE_GRAD: + case AngleType::Gradians: if (rat_gt(*pa, rat_200, precision)) { PRAT ptmp = nullptr; @@ -263,13 +263,13 @@ void tanrat(_Inout_ PRAT* px, uint32_t radix, int32_t precision) _tanrat(px, radix, precision); } -void tananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t precision) +void tananglerat(_Inout_ PRAT* pa, AngleType angletype, uint32_t radix, int32_t precision) { scalerat(pa, angletype, radix, precision); switch (angletype) { - case ANGLE_DEG: + case AngleType::Degrees: if (rat_gt(*pa, rat_180, precision)) { subrat(pa, rat_180, precision); @@ -277,7 +277,7 @@ void tananglerat(_Inout_ PRAT* pa, ANGLE_TYPE angletype, uint32_t radix, int32_t divrat(pa, rat_180, precision); mulrat(pa, pi, precision); break; - case ANGLE_GRAD: + case AngleType::Gradians: if (rat_gt(*pa, rat_200, precision)) { subrat(pa, rat_200, precision); diff --git a/src/CalcManager/UnitConverter.cpp b/src/CalcManager/UnitConverter.cpp index ce0ebb81..3d4b1865 100644 --- a/src/CalcManager/UnitConverter.cpp +++ b/src/CalcManager/UnitConverter.cpp @@ -13,19 +13,19 @@ using namespace std; using namespace UnitConversionManager; using namespace CalcManager::NumberFormattingUtils; -static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3; -static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6; -static constexpr uint32_t EXPECTEDSTATEDATATOKENCOUNT = 5; -static constexpr uint32_t EXPECTEDMAPCOMPONENTTOKENCOUNT = 2; +static constexpr uint32_t EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT = 3U; +static constexpr uint32_t EXPECTEDSERIALIZEDUNITTOKENCOUNT = 6U; +static constexpr uint32_t EXPECTEDSTATEDATATOKENCOUNT = 5U; +static constexpr uint32_t EXPECTEDMAPCOMPONENTTOKENCOUNT = 2U; -static constexpr int32_t MAXIMUMDIGITSALLOWED = 15; -static constexpr int32_t OPTIMALDIGITSALLOWED = 7; +static constexpr uint32_t MAXIMUMDIGITSALLOWED = 15U; +static constexpr uint32_t OPTIMALDIGITSALLOWED = 7U; static constexpr wchar_t LEFTESCAPECHAR = L'{'; static constexpr wchar_t RIGHTESCAPECHAR = L'}'; -static const double OPTIMALDECIMALALLOWED = pow(10, -1 * (OPTIMALDIGITSALLOWED - 1)); -static const double MINIMUMDECIMALALLOWED = pow(10, -1 * (MAXIMUMDIGITSALLOWED - 1)); +static const double OPTIMALDECIMALALLOWED = 1e-6; // pow(10, -1 * (OPTIMALDIGITSALLOWED - 1)); +static const double MINIMUMDECIMALALLOWED = 1e-14; // pow(10, -1 * (MAXIMUMDIGITSALLOWED - 1)); unordered_map quoteConversions; unordered_map unquoteConversions; @@ -109,7 +109,7 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i { if (m_currentCategory.id != input.id) { - for (auto& unit : m_categoryToUnits[m_currentCategory]) + for (auto& unit : m_categoryToUnits[m_currentCategory.id]) { unit.isConversionSource = (unit.id == m_fromType.id); unit.isConversionTarget = (unit.id == m_toType.id); @@ -121,7 +121,7 @@ CategorySelectionInitializer UnitConverter::SetCurrentCategory(const Category& i } } - newUnitList = m_categoryToUnits[input]; + newUnitList = m_categoryToUnits[input.id]; } InitializeSelectedUnits(); @@ -149,6 +149,11 @@ void UnitConverter::SetCurrentUnitTypes(const Unit& fromType, const Unit& toType return; } + if (m_fromType != fromType) + { + m_switchedActive = true; + } + m_fromType = fromType; m_toType = toType; Calculate(); @@ -191,6 +196,11 @@ void UnitConverter::SwitchActive(const wstring& newValue) } } +bool UnitConversionManager::UnitConverter::IsSwitchedActive() const +{ + return m_switchedActive; +} + wstring UnitConverter::CategoryToString(const Category& c, wstring_view delimiter) { return Quote(std::to_wstring(c.id)) @@ -209,7 +219,7 @@ vector UnitConverter::StringToVector(wstring_view w, wstring_view delim while (delimiterIndex != wstring_view::npos) { serializedTokens.emplace_back(w.substr(startIndex, delimiterIndex - startIndex)); - startIndex = delimiterIndex + (int)delimiter.size(); + startIndex = delimiterIndex + static_cast(delimiter.size()); delimiterIndex = w.find(delimiter, startIndex); } if (addRemainder) @@ -244,9 +254,9 @@ Unit UnitConverter::StringToUnit(wstring_view w) serializedUnit.name = Unquote(tokenList[1]); serializedUnit.accessibleName = serializedUnit.name; serializedUnit.abbreviation = Unquote(tokenList[2]); - serializedUnit.isConversionSource = (tokenList[3].compare(L"1") == 0); - serializedUnit.isConversionTarget = (tokenList[4].compare(L"1") == 0); - serializedUnit.isWhimsical = (tokenList[5].compare(L"1") == 0); + serializedUnit.isConversionSource = (tokenList[3] == L"1"); + serializedUnit.isConversionTarget = (tokenList[4] == L"1"); + serializedUnit.isWhimsical = (tokenList[5] == L"1"); return serializedUnit; } @@ -256,7 +266,7 @@ Category UnitConverter::StringToCategory(wstring_view w) assert(tokenList.size() == EXPECTEDSERIALIZEDCATEGORYTOKENCOUNT); Category serializedCategory; serializedCategory.id = wcstol(Unquote(tokenList[0]).c_str(), nullptr, 10); - serializedCategory.supportsNegative = (tokenList[1].compare(L"1") == 0); + serializedCategory.supportsNegative = (tokenList[1] == L"1"); serializedCategory.name = Unquote(tokenList[2]); return serializedCategory; } @@ -283,7 +293,7 @@ void UnitConverter::RestoreUserPreferences(wstring_view userPreferences) m_currentCategory = StringToCategory(outerTokens[2]); // Only restore from the saved units if they are valid in the current available units. - auto itr = m_categoryToUnits.find(m_currentCategory); + auto itr = m_categoryToUnits.find(m_currentCategory.id); if (itr != m_categoryToUnits.end()) { const auto& curUnits = itr->second; @@ -559,7 +569,7 @@ future> UnitConverter::RefreshCurrencyRatios() } shared_future sharedLoadResult = loadDataResult.share(); - return async([this, currencyDataLoader, sharedLoadResult]() { + return async([currencyDataLoader, sharedLoadResult]() { sharedLoadResult.wait(); bool didLoad = sharedLoadResult.get(); wstring timestamp; @@ -618,10 +628,10 @@ vector> UnitConverter::CalculateSuggested() newEntry.magnitude = log10(convertedValue); newEntry.value = convertedValue; newEntry.type = cur.first; - if (newEntry.type.isWhimsical == false) - intermediateVector.push_back(newEntry); - else + if (newEntry.type.isWhimsical) intermediateWhimsicalVector.push_back(newEntry); + else + intermediateVector.push_back(newEntry); } } @@ -643,15 +653,15 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificantDigits(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2U); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificantDigits(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1U); } else { - roundedString = RoundSignificantDigits(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0U); } if (stod(roundedString) != 0.0 || m_currentCategory.supportsNegative) { @@ -681,15 +691,15 @@ vector> UnitConverter::CalculateSuggested() wstring roundedString; if (abs(entry.value) < 100) { - roundedString = RoundSignificantDigits(entry.value, 2); + roundedString = RoundSignificantDigits(entry.value, 2U); } else if (abs(entry.value) < 1000) { - roundedString = RoundSignificantDigits(entry.value, 1); + roundedString = RoundSignificantDigits(entry.value, 1U); } else { - roundedString = RoundSignificantDigits(entry.value, 0); + roundedString = RoundSignificantDigits(entry.value, 0U); } // How to work out which is the best whimsical value to add to the vector? @@ -713,10 +723,8 @@ vector> UnitConverter::CalculateSuggested() /// void UnitConverter::ResetCategoriesAndRatios() { - m_categories = m_dataLoader->LoadOrderedCategories(); - m_switchedActive = false; - + m_categories = m_dataLoader->GetOrderedCategories(); if (m_categories.empty()) { return; @@ -738,8 +746,8 @@ void UnitConverter::ResetCategoriesAndRatios() continue; } - vector units = activeDataLoader->LoadOrderedUnits(category); - m_categoryToUnits[category] = units; + vector units = activeDataLoader->GetOrderedUnits(category); + m_categoryToUnits[category.id] = units; // Just because the units are empty, doesn't mean the user can't select this category, // we just want to make sure we don't let an unready category be the default. @@ -789,7 +797,7 @@ void UnitConverter::InitializeSelectedUnits() return; } - auto itr = m_categoryToUnits.find(m_currentCategory); + auto itr = m_categoryToUnits.find(m_currentCategory.id); if (itr == m_categoryToUnits.end()) { return; @@ -800,8 +808,8 @@ void UnitConverter::InitializeSelectedUnits() { // 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(); + const bool isFromUnitValid = m_fromType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_fromType) != curUnits.end(); + const bool isToUnitValid = m_toType != EMPTY_UNIT && find(curUnits.begin(), curUnits.end(), m_toType) != curUnits.end(); if (isFromUnitValid && isToUnitValid) { @@ -877,9 +885,9 @@ void UnitConverter::Calculate() else { double currentValue = stod(m_currentDisplay); - double returnValue = Convert(currentValue, conversionTable[m_toType]); + const double returnValue = Convert(currentValue, conversionTable[m_toType]); - auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory); + const auto isCurrencyConverter = m_currencyDataLoader != nullptr && m_currencyDataLoader->SupportsCategory(this->m_currentCategory); if (isCurrencyConverter) { // We don't need to trim the value when it's a currency. @@ -888,15 +896,15 @@ void UnitConverter::Calculate() } else { - int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue); + const unsigned int numPreDecimal = GetNumberDigitsWholeNumberPart(returnValue); if (numPreDecimal > MAXIMUMDIGITSALLOWED || (returnValue != 0 && abs(returnValue) < MINIMUMDECIMALALLOWED)) { m_returnDisplay = ToScientificNumber(returnValue); } else { - int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay); - int precision = 0; + const unsigned int currentNumberSignificantDigits = GetNumberDigits(m_currentDisplay); + unsigned int precision; if (abs(returnValue) < OPTIMALDECIMALALLOWED) { precision = MAXIMUMDIGITSALLOWED; @@ -905,7 +913,8 @@ void UnitConverter::Calculate() { // 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); + auto numberDigits = max(OPTIMALDIGITSALLOWED, min(MAXIMUMDIGITSALLOWED, currentNumberSignificantDigits)); + precision = numberDigits > numPreDecimal ? numberDigits - numPreDecimal : 0; } m_returnDisplay = RoundSignificantDigits(returnValue, precision); diff --git a/src/CalcManager/UnitConverter.h b/src/CalcManager/UnitConverter.h index 9d65324e..fe085dcb 100644 --- a/src/CalcManager/UnitConverter.h +++ b/src/CalcManager/UnitConverter.h @@ -6,8 +6,8 @@ #include #include #include -#include "sal_cross_platform.h" // for SAL -#include // for std::shared_ptr +#include "sal_cross_platform.h" // for SAL +#include // for std::shared_ptr namespace UnitConversionManager { @@ -18,11 +18,11 @@ namespace UnitConversionManager Unit() { } - Unit(int id, std::wstring name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical) + Unit(int id, std::wstring_view name, std::wstring abbreviation, bool isConversionSource, bool isConversionTarget, bool isWhimsical) : id(id) , name(name) , accessibleName(name) - , abbreviation(abbreviation) + , abbreviation(std::move(abbreviation)) , isConversionSource(isConversionSource) , isConversionTarget(isConversionTarget) , isWhimsical(isWhimsical) @@ -31,23 +31,26 @@ namespace UnitConversionManager Unit( int id, - std::wstring currencyName, - std::wstring countryName, + std::wstring_view currencyName, + std::wstring_view countryName, std::wstring abbreviation, bool isRtlLanguage, bool isConversionSource, bool isConversionTarget) : id(id) - , abbreviation(abbreviation) + , abbreviation(std::move(abbreviation)) , isConversionSource(isConversionSource) , isConversionTarget(isConversionTarget) , isWhimsical(false) { - std::wstring nameValue1 = isRtlLanguage ? currencyName : countryName; - std::wstring nameValue2 = isRtlLanguage ? countryName : currencyName; + auto nameValue1 = isRtlLanguage ? currencyName : countryName; + auto nameValue2 = isRtlLanguage ? countryName : currencyName; - name = nameValue1 + L" - " + nameValue2; - accessibleName = nameValue1 + L" " + nameValue2; + name = nameValue1; + name.append(L" - ").append(nameValue2); + + accessibleName = nameValue1; + accessibleName.append(1, L' ').append(nameValue2); } int id; @@ -84,7 +87,7 @@ namespace UnitConversionManager Category(int id, std::wstring name, bool supportsNegative) : id(id) - , name(name) + , name(std::move(name)) , supportsNegative(supportsNegative) { } @@ -113,15 +116,6 @@ namespace UnitConversionManager } }; - class CategoryHash - { - public: - size_t operator()(const Category& x) const - { - return x.id; - } - }; - struct SuggestedValueIntermediate { double magnitude; @@ -168,7 +162,7 @@ namespace UnitConversionManager std::unordered_map, UnitConversionManager::UnitHash> UnitToUnitToConversionDataMap; - typedef std::unordered_map, UnitConversionManager::CategoryHash> + typedef std::unordered_map> CategoryToUnitVectorMap; class IViewModelCurrencyCallback @@ -187,8 +181,8 @@ namespace UnitConversionManager public: virtual ~IConverterDataLoader(){}; virtual void LoadData() = 0; // prepare data if necessary before calling other functions - virtual std::vector LoadOrderedCategories() = 0; - virtual std::vector LoadOrderedUnits(const Category& c) = 0; + virtual std::vector GetOrderedCategories() = 0; + virtual std::vector GetOrderedUnits(const Category& c) = 0; virtual std::unordered_map LoadOrderedRatios(const Unit& u) = 0; virtual bool SupportsCategory(const Category& target) = 0; }; @@ -229,6 +223,7 @@ namespace UnitConversionManager virtual Category GetCurrentCategory() = 0; virtual void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) = 0; virtual void SwitchActive(const std::wstring& newValue) = 0; + virtual bool IsSwitchedActive() const = 0; virtual std::wstring SaveUserPreferences() = 0; virtual void RestoreUserPreferences(_In_ std::wstring_view userPreferences) = 0; virtual void SendCommand(Command command) = 0; @@ -252,6 +247,7 @@ namespace UnitConversionManager Category GetCurrentCategory() override; void SetCurrentUnitTypes(const Unit& fromType, const Unit& toType) override; void SwitchActive(const std::wstring& newValue) override; + bool IsSwitchedActive() const override; std::wstring SaveUserPreferences() override; void RestoreUserPreferences(std::wstring_view userPreference) override; void SendCommand(Command command) override; diff --git a/src/CalcManager/pch.h b/src/CalcManager/pch.h index 288bbc91..9c71ff3a 100644 --- a/src/CalcManager/pch.h +++ b/src/CalcManager/pch.h @@ -21,6 +21,6 @@ #include #include #include -#include +#include #include #include diff --git a/src/CalcViewModel/ApplicationViewModel.cpp b/src/CalcViewModel/ApplicationViewModel.cpp index efe9bca8..46855071 100644 --- a/src/CalcViewModel/ApplicationViewModel.cpp +++ b/src/CalcViewModel/ApplicationViewModel.cpp @@ -43,6 +43,7 @@ namespace ApplicationViewModel::ApplicationViewModel() : m_CalculatorViewModel(nullptr) , m_DateCalcViewModel(nullptr) + , m_GraphingCalcViewModel(nullptr) , m_ConverterViewModel(nullptr) , m_PreviousMode(ViewMode::None) , m_mode(ViewMode::None) @@ -74,7 +75,7 @@ void ApplicationViewModel::Categories::set(IObservableVector void ApplicationViewModel::Initialize(ViewMode mode) { - if (!NavCategory::IsValidViewMode(mode)) + if (!NavCategory::IsValidViewMode(mode) || !NavCategory::IsViewModeEnabled(mode)) { mode = ViewMode::Standard; } @@ -132,6 +133,13 @@ void ApplicationViewModel::OnModeChanged() } m_CalculatorViewModel->SetCalculatorType(m_mode); } + else if (NavCategory::IsGraphingCalculatorViewMode(m_mode)) + { + if (!m_GraphingCalcViewModel) + { + m_GraphingCalcViewModel = ref new GraphingCalculatorViewModel(); + } + } else if (NavCategory::IsDateCalculatorViewMode(m_mode)) { if (!m_DateCalcViewModel) @@ -182,7 +190,7 @@ void ApplicationViewModel::OnCopyCommand(Object ^ parameter) { DateCalcViewModel->OnCopyCommand(parameter); } - else + else if (NavCategory::IsCalculatorViewMode(m_mode)) { CalculatorViewModel->OnCopyCommand(parameter); } @@ -223,7 +231,6 @@ task ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float heig 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; @@ -252,7 +259,6 @@ task ApplicationViewModel::HandleToggleAlwaysOnTop(float width, float heig } bool success = co_await ApplicationView::GetForCurrentView()->TryEnterViewModeAsync(ApplicationViewMode::CompactOverlay, compactOptions); - CalculatorViewModel->AreHistoryShortcutsEnabled = !success; CalculatorViewModel->HistoryVM->AreHistoryShortcutsEnabled = !success; CalculatorViewModel->IsAlwaysOnTop = success; IsAlwaysOnTop = success; diff --git a/src/CalcViewModel/ApplicationViewModel.h b/src/CalcViewModel/ApplicationViewModel.h index 8a293eac..7e4238d8 100644 --- a/src/CalcViewModel/ApplicationViewModel.h +++ b/src/CalcViewModel/ApplicationViewModel.h @@ -5,6 +5,7 @@ #include "StandardCalculatorViewModel.h" #include "DateCalculatorViewModel.h" +#include "GraphingCalculator/GraphingCalculatorViewModel.h" #include "UnitConverterViewModel.h" namespace CalculatorApp @@ -21,6 +22,7 @@ namespace CalculatorApp OBSERVABLE_OBJECT(); OBSERVABLE_PROPERTY_RW(StandardCalculatorViewModel ^, CalculatorViewModel); OBSERVABLE_PROPERTY_RW(DateCalculatorViewModel ^, DateCalcViewModel); + OBSERVABLE_PROPERTY_RW(GraphingCalculatorViewModel ^, GraphingCalcViewModel); OBSERVABLE_PROPERTY_RW(UnitConverterViewModel ^, ConverterViewModel); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, PreviousMode); OBSERVABLE_PROPERTY_R(bool, IsAlwaysOnTop); diff --git a/src/CalcViewModel/CalcViewModel.vcxproj b/src/CalcViewModel/CalcViewModel.vcxproj index 49bc036f..c2a69b5c 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj +++ b/src/CalcViewModel/CalcViewModel.vcxproj @@ -122,29 +122,9 @@ - - false - - - false - - - false - - - false - - - false - - - false - - - false - - + false + true @@ -313,16 +293,13 @@ - - - @@ -330,7 +307,6 @@ - @@ -338,16 +314,19 @@ - - + + + + + @@ -361,16 +340,13 @@ - - - @@ -380,6 +356,9 @@ + + + @@ -400,6 +379,12 @@ {311e866d-8b93-4609-a691-265941fee101} + + {e727a92b-f149-492c-8117-c039a298719b} + + + {fc81ff41-02cd-4cd9-9bc5-45a1e39ac6ed} + @@ -420,16 +405,7 @@ - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + \ No newline at end of file diff --git a/src/CalcViewModel/CalcViewModel.vcxproj.filters b/src/CalcViewModel/CalcViewModel.vcxproj.filters index ac5d41d3..b219806e 100644 --- a/src/CalcViewModel/CalcViewModel.vcxproj.filters +++ b/src/CalcViewModel/CalcViewModel.vcxproj.filters @@ -10,6 +10,9 @@ {0184f727-b8aa-4af8-a699-63f1b56e7853} + + {cf7dca32-9727-4f98-83c3-1c0ca7dd1e0c} + @@ -23,18 +26,12 @@ Common - - Common - Common Common - - Common - Common @@ -50,9 +47,6 @@ Common - - Common - Common @@ -80,9 +74,18 @@ DataLoaders + + GraphingCalculator + + + GraphingCalculator + Common\Automation + + GraphingCalculator + @@ -94,15 +97,9 @@ - - Common - Common - - Common - Common @@ -112,18 +109,12 @@ Common - - Common - Common Common - - Common - Common @@ -136,9 +127,6 @@ Common - - Common - Common @@ -163,9 +151,6 @@ Common - - Common - Common\Automation @@ -184,9 +169,6 @@ DataLoaders - - Common - DataLoaders @@ -199,12 +181,29 @@ Common + + GraphingCalculator + + + GraphingCalculator + + + Common + + + GraphingCalculator + + + GraphingCalculator + + + Common + DataLoaders - diff --git a/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h b/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h new file mode 100644 index 00000000..9f947f41 --- /dev/null +++ b/src/CalcViewModel/Common/Automation/INarratorAnnouncementHost.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "NarratorAnnouncement.h" + +// Declaration of the INarratorAnnouncementHost interface. +// This interface exists to hide the concrete announcement host +// being used. Depending on the version of the OS the app is running on, +// the app may need a host that uses LiveRegionChanged or RaiseNotification. + +namespace CalculatorApp::Common::Automation +{ +public + interface class INarratorAnnouncementHost + { + public: + // Is the host available on this OS. + bool IsHostAvailable(); + + // Make a new instance of a concrete host. + INarratorAnnouncementHost ^ MakeHost(); + + // Make an announcement using the concrete host's preferred method. + void Announce(NarratorAnnouncement ^ announcement); + }; +} diff --git a/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp b/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp new file mode 100644 index 00000000..0df3a96c --- /dev/null +++ b/src/CalcViewModel/Common/Automation/LiveRegionHost.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "LiveRegionHost.h" + +using namespace CalculatorApp::Common::Automation; +using namespace Windows::UI::Xaml::Automation; +using namespace Windows::UI::Xaml::Automation::Peers; +using namespace Windows::UI::Xaml::Controls; + +LiveRegionHost::LiveRegionHost() + : m_host(nullptr) +{ +} + +bool LiveRegionHost::IsHostAvailable() +{ + // LiveRegion is always available. + return true; +} + +INarratorAnnouncementHost ^ LiveRegionHost::MakeHost() +{ + return ref new LiveRegionHost(); +} + +void LiveRegionHost::Announce(NarratorAnnouncement ^ announcement) +{ + if (m_host == nullptr) + { + m_host = ref new TextBlock(); + AutomationProperties::SetLiveSetting(m_host, AutomationLiveSetting::Assertive); + } + + AutomationProperties::SetName(m_host, announcement->Announcement); + AutomationPeer ^ peer = FrameworkElementAutomationPeer::FromElement(m_host); + if (peer != nullptr) + { + peer->RaiseAutomationEvent(AutomationEvents::LiveRegionChanged); + } +} diff --git a/src/CalcViewModel/Common/Automation/LiveRegionHost.h b/src/CalcViewModel/Common/Automation/LiveRegionHost.h new file mode 100644 index 00000000..fef7c714 --- /dev/null +++ b/src/CalcViewModel/Common/Automation/LiveRegionHost.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "INarratorAnnouncementHost.h" + +// Declaration of the LiveRegionHost class. +// This class announces NarratorAnnouncements using the LiveRegionChanged event. +// This event is unreliable and should be deprecated in favor of the new +// RaiseNotification API in RS3. + +namespace CalculatorApp::Common::Automation +{ + // This class exists so that the app can run on RS2 and use LiveRegions + // to host notifications on those builds. + // When the app switches to min version RS3, this class can be removed + // and the app will switch to using the Notification API. + // TODO - MSFT 12735088 +public + ref class LiveRegionHost sealed : public INarratorAnnouncementHost + { + public: + LiveRegionHost(); + + virtual bool IsHostAvailable(); + virtual INarratorAnnouncementHost ^ MakeHost(); + + virtual void Announce(NarratorAnnouncement ^ announcement); + + private: + Windows::UI::Xaml::UIElement ^ m_host; + }; +} diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp index 940c88c9..5365937d 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.cpp @@ -18,11 +18,18 @@ namespace CalculatorApp::Common::Automation StringReference MemoryItemChanged(L"MemorySlotChanged"); StringReference MemoryItemAdded(L"MemorySlotAdded"); StringReference HistoryCleared(L"HistoryCleared"); + StringReference HistorySlotCleared(L"HistorySlotCleared"); StringReference CategoryNameChanged(L"CategoryNameChanged"); StringReference UpdateCurrencyRates(L"UpdateCurrencyRates"); StringReference DisplayCopied(L"DisplayCopied"); StringReference OpenParenthesisCountChanged(L"OpenParenthesisCountChanged"); StringReference NoParenthesisAdded(L"NoParenthesisAdded"); + StringReference GraphModeChanged(L"GraphModeChanged"); + StringReference GraphViewChanged(L"GraphViewChanged"); + StringReference FunctionRemoved(L"FunctionRemoved"); + StringReference GraphViewBestFitChanged(L"GraphViewBestFitChanged"); + StringReference AlwaysOnTop(L"AlwaysOnTop"); + StringReference BitShiftRadioButtonContent(L"BitShiftRadioButtonContent"); } } @@ -99,6 +106,15 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetHistoryClearedAnnouncement(Str announcement, CalculatorActivityIds::HistoryCleared, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::MostRecent); } +NarratorAnnouncement ^ CalculatorAnnouncement::GetHistorySlotClearedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, + CalculatorActivityIds::HistorySlotCleared, + AutomationNotificationKind::ItemRemoved, + AutomationNotificationProcessing::ImportantMostRecent); +} + NarratorAnnouncement ^ CalculatorAnnouncement::GetCategoryNameChangedAnnouncement(String ^ announcement) { return ref new NarratorAnnouncement( @@ -140,3 +156,51 @@ NarratorAnnouncement ^ CalculatorAnnouncement::GetNoRightParenthesisAddedAnnounc AutomationNotificationKind::ActionCompleted, AutomationNotificationProcessing::ImportantMostRecent); } + +NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphModeChangedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, + CalculatorActivityIds::GraphModeChanged, + AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::ImportantMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphViewChangedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, + CalculatorActivityIds::GraphViewChanged, + AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::CurrentThenMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetFunctionRemovedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::FunctionRemoved, AutomationNotificationKind::ItemRemoved, AutomationNotificationProcessing::MostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetGraphViewBestFitChangedAnnouncement(Platform::String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, + CalculatorActivityIds::GraphViewBestFitChanged, + AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::MostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetAlwaysOnTopChangedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, CalculatorActivityIds::AlwaysOnTop, AutomationNotificationKind::ActionCompleted, AutomationNotificationProcessing::ImportantMostRecent); +} + +NarratorAnnouncement ^ CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(String ^ announcement) +{ + return ref new NarratorAnnouncement( + announcement, + CalculatorActivityIds::BitShiftRadioButtonContent, + AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::ImportantMostRecent); +} diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h index fef7e260..a19de86f 100644 --- a/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncement.h @@ -57,6 +57,7 @@ public static NarratorAnnouncement ^ GetMemoryItemAddedAnnouncement(Platform::String ^ announcement); static NarratorAnnouncement ^ GetHistoryClearedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetHistorySlotClearedAnnouncement(Platform::String ^ announcement); static NarratorAnnouncement ^ GetCategoryNameChangedAnnouncement(Platform::String ^ announcement); @@ -66,5 +67,16 @@ public static NarratorAnnouncement ^ GetOpenParenthesisCountChangedAnnouncement(Platform::String ^ announcement); static NarratorAnnouncement ^ GetNoRightParenthesisAddedAnnouncement(Platform::String ^ announcement); + + static NarratorAnnouncement ^ GetGraphModeChangedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetGraphViewChangedAnnouncement(Platform::String ^ announcement); + static NarratorAnnouncement ^ GetGraphViewBestFitChangedAnnouncement(Platform::String ^ announcement); + + static NarratorAnnouncement ^ GetFunctionRemovedAnnouncement(Platform::String ^ announcement); + + static NarratorAnnouncement ^ GetAlwaysOnTopChangedAnnouncement(Platform::String ^ announcement); + + static NarratorAnnouncement ^ GetBitShiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement); + }; } diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp new file mode 100644 index 00000000..a103c7e7 --- /dev/null +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "NarratorAnnouncementHostFactory.h" +#include "NotificationHost.h" +#include "LiveRegionHost.h" + +using namespace CalculatorApp::Common::Automation; +using namespace std; + +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::s_hostProducer; +vector NarratorAnnouncementHostFactory::s_hosts; + +// This static variable is used only to call the initialization function, to initialize the other static variables. +int NarratorAnnouncementHostFactory::s_init = NarratorAnnouncementHostFactory::Initialize(); +int NarratorAnnouncementHostFactory::Initialize() +{ + RegisterHosts(); + NarratorAnnouncementHostFactory::s_hostProducer = GetHostProducer(); + + return 0; +} + +// For now, there are two type of announcement hosts. +// We'd prefer to use Notification if it's available and fall back to LiveRegion +// if not. The availability of the host depends on the version of the OS the app is running on. +// When the app switches to min version RS3, the LiveRegionHost can be removed and we will always +// use NotificationHost. +// TODO - MSFT 12735088 +void NarratorAnnouncementHostFactory::RegisterHosts() +{ + // The host that will be used is the first available host, + // therefore, order of hosts is important here. + NarratorAnnouncementHostFactory::s_hosts = { ref new NotificationHost(), ref new LiveRegionHost() }; +} + +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::GetHostProducer() +{ + for (INarratorAnnouncementHost ^ host : NarratorAnnouncementHostFactory::s_hosts) + { + if (host->IsHostAvailable()) + { + return host; + } + } + + assert(false && L"No suitable AnnouncementHost was found."); + return nullptr; +} + +INarratorAnnouncementHost ^ NarratorAnnouncementHostFactory::MakeHost() +{ + if (NarratorAnnouncementHostFactory::s_hostProducer == nullptr) + { + assert(false && L"No host producer has been assigned."); + return nullptr; + } + + return NarratorAnnouncementHostFactory::s_hostProducer->MakeHost(); +} diff --git a/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h new file mode 100644 index 00000000..4b739a79 --- /dev/null +++ b/src/CalcViewModel/Common/Automation/NarratorAnnouncementHostFactory.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "INarratorAnnouncementHost.h" + +// Declaration of the NarratorAnnouncementHostFactory class. +// This class exists to hide the construction of a concrete INarratorAnnouncementHost. +// Depending on the version of the OS the app is running on, the factory will return +// an announcement host appropriate for that version. + +namespace CalculatorApp::Common::Automation +{ + class NarratorAnnouncementHostFactory + { + public: + static INarratorAnnouncementHost ^ MakeHost(); + + private: + NarratorAnnouncementHostFactory() + { + } + + static int Initialize(); + static void RegisterHosts(); + static INarratorAnnouncementHost ^ GetHostProducer(); + + private: + static int s_init; + static INarratorAnnouncementHost ^ s_hostProducer; + static std::vector s_hosts; + }; +} diff --git a/src/CalcViewModel/Common/Automation/NotificationHost.cpp b/src/CalcViewModel/Common/Automation/NotificationHost.cpp new file mode 100644 index 00000000..92bf846e --- /dev/null +++ b/src/CalcViewModel/Common/Automation/NotificationHost.cpp @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "NotificationHost.h" + +using namespace CalculatorApp::Common::Automation; +using namespace Windows::Foundation::Metadata; +using namespace Windows::UI::Xaml::Automation; +using namespace Windows::UI::Xaml::Automation::Peers; +using namespace Windows::UI::Xaml::Controls; + +NotificationHost::NotificationHost() + : m_host(nullptr) +{ +} + +bool NotificationHost::IsHostAvailable() +{ + return ApiInformation::IsMethodPresent(L"Windows.UI.Xaml.Automation.Peers.AutomationPeer", L"RaiseNotificationEvent"); +} + +INarratorAnnouncementHost ^ NotificationHost::MakeHost() +{ + return ref new NotificationHost(); +} + +void NotificationHost::Announce(NarratorAnnouncement ^ announcement) +{ + if (m_host == nullptr) + { + m_host = ref new TextBlock(); + } + + auto peer = FrameworkElementAutomationPeer::FromElement(m_host); + if (peer != nullptr) + { + peer->RaiseNotificationEvent( + GetWindowsNotificationKind(announcement->Kind), + GetWindowsNotificationProcessing(announcement->Processing), + announcement->Announcement, + announcement->ActivityId); + } +} + +StandardPeers::AutomationNotificationKind NotificationHost::GetWindowsNotificationKind(CustomPeers::AutomationNotificationKind customKindType) +{ + switch (customKindType) + { + case CustomPeers::AutomationNotificationKind::ItemAdded: + return StandardPeers::AutomationNotificationKind::ItemAdded; + + case CustomPeers::AutomationNotificationKind::ItemRemoved: + return StandardPeers::AutomationNotificationKind::ItemRemoved; + + case CustomPeers::AutomationNotificationKind::ActionCompleted: + return StandardPeers::AutomationNotificationKind::ActionCompleted; + + case CustomPeers::AutomationNotificationKind::ActionAborted: + return StandardPeers::AutomationNotificationKind::ActionAborted; + + case CustomPeers::AutomationNotificationKind::Other: + return StandardPeers::AutomationNotificationKind::Other; + + default: + assert(false && L"Unexpected AutomationNotificationKind"); + } + + return StandardPeers::AutomationNotificationKind::Other; +} + +StandardPeers::AutomationNotificationProcessing +NotificationHost::GetWindowsNotificationProcessing(CustomPeers::AutomationNotificationProcessing customProcessingType) +{ + switch (customProcessingType) + { + case CustomPeers::AutomationNotificationProcessing::ImportantAll: + return StandardPeers::AutomationNotificationProcessing::ImportantAll; + + case CustomPeers::AutomationNotificationProcessing::ImportantMostRecent: + return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent; + + case CustomPeers::AutomationNotificationProcessing::All: + return StandardPeers::AutomationNotificationProcessing::All; + + case CustomPeers::AutomationNotificationProcessing::MostRecent: + return StandardPeers::AutomationNotificationProcessing::MostRecent; + + case CustomPeers::AutomationNotificationProcessing::CurrentThenMostRecent: + return StandardPeers::AutomationNotificationProcessing::CurrentThenMostRecent; + + default: + assert(false && L"Unexpected AutomationNotificationProcessing"); + } + + return StandardPeers::AutomationNotificationProcessing::ImportantMostRecent; +} diff --git a/src/CalcViewModel/Common/Automation/NotificationHost.h b/src/CalcViewModel/Common/Automation/NotificationHost.h new file mode 100644 index 00000000..d0a929c6 --- /dev/null +++ b/src/CalcViewModel/Common/Automation/NotificationHost.h @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "INarratorAnnouncementHost.h" + +// Declaration of the NotificationHost class. +// This class announces NarratorAnnouncements using the RaiseNotification API +// available in RS3. + +namespace CalculatorApp::Common::Automation +{ +public + ref class NotificationHost sealed : public INarratorAnnouncementHost + { + public: + NotificationHost(); + + virtual bool IsHostAvailable(); + virtual INarratorAnnouncementHost ^ MakeHost(); + + virtual void Announce(NarratorAnnouncement ^ announcement); + + private: + static Windows::UI::Xaml::Automation::Peers::AutomationNotificationKind + GetWindowsNotificationKind(CalculatorApp::Common::Automation::AutomationNotificationKind customKindType); + + static Windows::UI::Xaml::Automation::Peers::AutomationNotificationProcessing + GetWindowsNotificationProcessing(CalculatorApp::Common::Automation::AutomationNotificationProcessing customProcessingType); + + private: + Windows::UI::Xaml::UIElement ^ m_host; + }; +} diff --git a/src/CalcViewModel/Common/BindableBase.cpp b/src/CalcViewModel/Common/BindableBase.cpp deleted file mode 100644 index ccba7985..00000000 --- a/src/CalcViewModel/Common/BindableBase.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "pch.h" -#include "BindableBase.h" - -using namespace CalculatorApp::Common; - -using namespace Platform; -using namespace Windows::UI::Xaml::Data; - -/// -/// Notifies listeners that a property value has changed. -/// -/// Name of the property used to notify listeners. -void BindableBase::OnPropertyChanged(String ^ propertyName) -{ - PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName)); -} - -Windows::UI::Xaml::Data::ICustomProperty ^ BindableBase::GetCustomProperty(Platform::String ^ name) -{ - return nullptr; -} - -Windows::UI::Xaml::Data::ICustomProperty ^ BindableBase::GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type) -{ - return nullptr; -} - -Platform::String ^ BindableBase::GetStringRepresentation() -{ - return this->ToString(); -} diff --git a/src/CalcViewModel/Common/BindableBase.h b/src/CalcViewModel/Common/BindableBase.h deleted file mode 100644 index d1f958f5..00000000 --- a/src/CalcViewModel/Common/BindableBase.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - /// - /// Implementation of to simplify models. - /// - [Windows::Foundation::Metadata::WebHostHidden] public ref class BindableBase : Windows::UI::Xaml::DependencyObject, - Windows::UI::Xaml::Data::INotifyPropertyChanged, - Windows::UI::Xaml::Data::ICustomPropertyProvider - { - public: - virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; - - public: - // ICustomPropertyProvider - virtual Windows::UI::Xaml::Data::ICustomProperty ^ GetCustomProperty(Platform::String ^ name); - virtual Windows::UI::Xaml::Data::ICustomProperty ^ GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type); - virtual Platform::String ^ GetStringRepresentation(); - - property Windows::UI::Xaml::Interop::TypeName Type - { - virtual Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - protected: - virtual void OnPropertyChanged(Platform::String ^ propertyName); - }; - } -} diff --git a/src/CalcViewModel/Common/CalculatorButtonUser.h b/src/CalcViewModel/Common/CalculatorButtonUser.h index 113d34dc..a7b72189 100644 --- a/src/CalcViewModel/Common/CalculatorButtonUser.h +++ b/src/CalcViewModel/Common/CalculatorButtonUser.h @@ -109,7 +109,7 @@ public InvCoth = (int) CM::Command::CommandACOTH, CubeRoot = (int) CM::Command::CommandCUBEROOT, TwoPowerX = (int) CM::Command::CommandPOW2, - LogBaseX = (int) CM::Command::CommandLogBaseX, + LogBaseY = (int) CM::Command::CommandLogBaseY, Nand = (int) CM::Command::CommandNand, Nor = (int) CM::Command::CommandNor, Abs = (int) CM::Command::CommandAbs, @@ -120,7 +120,7 @@ public RshL = (int)CM::Command::CommandRSHFL, RolC = (int)CM::Command::CommandROLC, RorC = (int)CM::Command::CommandRORC, - + BINSTART = (int)CM::Command::CommandBINEDITSTART, BINPOS0 = (int)CM::Command::CommandBINPOS0, BINPOS1 = (int)CM::Command::CommandBINPOS1, @@ -194,6 +194,15 @@ public MemoryRecall = (int)CM::Command::CommandRECALL, MemoryClear = (int)CM::Command::CommandMCLEAR, BitflipButton = 1000, - FullKeypadButton = 1001 + FullKeypadButton = 1001, + + // Buttons used in graphing calculator + LessThan, + LessThanOrEqualTo, + GreaterThan, + GreaterThanOrEqualTo, + X, + Y, + Submit }; } diff --git a/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp b/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp deleted file mode 100644 index ec90228f..00000000 --- a/src/CalcViewModel/Common/ConversionResultTaskHelper.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "pch.h" -#include "ConversionResultTaskHelper.h" - -using namespace CalculatorApp::Common; -using namespace concurrency; -using namespace std; - -ConversionResultTaskHelper::ConversionResultTaskHelper(unsigned int delay, const function functionToRun) - : m_delay{ delay } - , m_storedFunction{ functionToRun } -{ - auto token = m_cts.get_token(); - auto delayTask = CompleteAfter(delay); - delayTask.then( - [this, token]() { - if (!token.is_canceled()) - { - m_storedFunction(); - } - }, - task_continuation_context::use_current()); -} - -ConversionResultTaskHelper::~ConversionResultTaskHelper() -{ - m_cts.cancel(); -} - -#pragma optimize("", off) -// Creates a task that completes after the specified delay. -// -// Taken from: How to: Create a Task that Completes After a Delay -// https://msdn.microsoft.com/en-us/library/hh873170.aspx -task ConversionResultTaskHelper::CompleteAfter(unsigned int timeout) -{ - co_await winrt::resume_after(winrt::Windows::Foundation::TimeSpan{ std::chrono::duration(timeout) }); -}; -#pragma optimize("", on) diff --git a/src/CalcViewModel/Common/ConversionResultTaskHelper.h b/src/CalcViewModel/Common/ConversionResultTaskHelper.h deleted file mode 100644 index 2fe543c3..00000000 --- a/src/CalcViewModel/Common/ConversionResultTaskHelper.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - class ConversionResultTaskHelper - { - public: - ConversionResultTaskHelper(unsigned int delay, const std::function functionToRun); - ~ConversionResultTaskHelper(); - - private: - concurrency::task CompleteAfter(unsigned int timeout); - - unsigned int m_delay; - concurrency::cancellation_token_source m_cts; - const std::function m_storedFunction; - }; - } -} diff --git a/src/CalcViewModel/Common/CopyPasteManager.cpp b/src/CalcViewModel/Common/CopyPasteManager.cpp index ade3a3fb..ae24aad1 100644 --- a/src/CalcViewModel/Common/CopyPasteManager.cpp +++ b/src/CalcViewModel/Common/CopyPasteManager.cpp @@ -310,6 +310,9 @@ bool CopyPasteManager::ExpressionRegExMatch( if (operandMatched) { + // Remember the sign of the operand + bool isNegativeValue = operand->Data()[0] == L'-'; + // Remove characters that are valid in the expression but we do not want to include in length calculations // or which will break conversion from string-to-ULL. auto operandValue = SanitizeOperand(operand); @@ -332,7 +335,11 @@ bool CopyPasteManager::ExpressionRegExMatch( break; } - if (operandAsULL->Value > maxOperandLengthAndValue.maxValue) + // Calculate how much we exceed the maxValue. + // In case we exceed it for 1 only, and working with negative number - that's a corner case for max signed values (e.g. -32768) + bool isOverflow = operandAsULL->Value > maxOperandLengthAndValue.maxValue; + bool isMaxNegativeValue = operandAsULL->Value - 1 == maxOperandLengthAndValue.maxValue; + if (isOverflow && !(isNegativeValue && isMaxNegativeValue)) { expMatched = false; break; @@ -594,9 +601,11 @@ ULONG32 CopyPasteManager::ProgrammerOperandLength(Platform::String ^ operand, Nu // Indian rupee(₹) - 8377 // pound(£) - 163 // euro(€) - 8364 +// non-breaking whitespace - 160 Platform::String ^ CopyPasteManager::RemoveUnwantedCharsFromString(Platform::String ^ input) { - constexpr wchar_t unWantedChars[] = { L' ', L',', L'"', 165, 164, 8373, 36, 8353, 8361, 8362, 8358, 8377, 163, 8364, 8234, 8235, 8236, 8237 }; + constexpr wchar_t unWantedChars[] = { L' ', L',', L'"', 165, 164, 8373, 36, 8353, 8361, 8362, 8358, 8377, 163, 8364, 8234, 8235, 8236, 8237, 160 }; + input = CalculatorApp::Common::LocalizationSettings::GetInstance().RemoveGroupSeparators(input); return ref new String(Utils::RemoveUnwantedCharsFromString(input->Data(), unWantedChars).c_str()); } diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp b/src/CalcViewModel/Common/KeyboardShortcutManager.cpp deleted file mode 100644 index 8990d5e8..00000000 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.cpp +++ /dev/null @@ -1,850 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "pch.h" -#include "KeyboardShortcutManager.h" -#include "AppResourceProvider.h" -#include "ApplicationViewModel.h" -#include "LocalizationSettings.h" - -using namespace Concurrency; -using namespace Platform; -using namespace std; -using namespace Windows::ApplicationModel::Resources; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::Foundation; -using namespace Windows::Foundation::Collections; -using namespace Windows::UI::Core; -using namespace Windows::UI::Xaml::Controls::Primitives; -using namespace Windows::System; -using namespace Utils; -using namespace CalculatorApp; -using namespace CalculatorApp::Common; -using namespace CalculatorApp::ViewModel; - -namespace MUXC = Microsoft::UI::Xaml::Controls; - -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, Character); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKey); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlChord); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyShiftChord); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyAltChord); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlShiftChord); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyInverseChord); -DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlInverseChord); - -static multimap> s_CharacterForButtons; -static multimap> s_VirtualKeysForButtons; -static multimap> s_VirtualKeyControlChordsForButtons; -static multimap> s_VirtualKeyShiftChordsForButtons; -static multimap> s_VirtualKeyAltChordsForButtons; -static multimap> s_VirtualKeyControlShiftChordsForButtons; -static multimap> s_VirtualKeyInverseChordsForButtons; -static multimap> s_VirtualKeyControlInverseChordsForButtons; - -static multimap s_ShiftKeyPressed; -static multimap s_ControlKeyPressed; -static multimap s_ShiftButtonChecked; -static multimap s_IsDropDownOpen; - -static reader_writer_lock s_keyboardShortcutMapLock; - -namespace CalculatorApp -{ - namespace Common - { - // Lights up all of the buttons in the given range - // The range is defined by a pair of iterators - template - void LightUpButtons(const T& buttons) - { - auto iterator = buttons.first; - for (; iterator != buttons.second; ++iterator) - { - auto button = iterator->second.Resolve(); - if (button && button->IsEnabled) - { - LightUpButton(button); - } - } - } - - void LightUpButton(ButtonBase ^ button) - { - // If the button is a toggle button then we don't need - // to change the UI of the button - if (dynamic_cast(button)) - { - return; - } - - // The button will go into the visual Pressed state with this call - VisualStateManager::GoToState(button, "Pressed", true); - - // This timer will fire after lightUpTime and make the button - // go back to the normal state. - // This timer will only fire once after which it will be destroyed - auto timer = ref new DispatcherTimer(); - TimeSpan lightUpTime{}; - lightUpTime.Duration = 500000L; // Half second (in 100-ns units) - timer->Interval = lightUpTime; - - WeakReference timerWeakReference(timer); - WeakReference buttonWeakReference(button); - timer->Tick += ref new EventHandler([buttonWeakReference, timerWeakReference](Object ^, Object ^) { - auto button = buttonWeakReference.Resolve(); - if (button) - { - VisualStateManager::GoToState(button, "Normal", true); - } - - // Cancel the timer after we're done so it only fires once - auto timer = timerWeakReference.Resolve(); - if (timer) - { - timer->Stop(); - } - }); - timer->Start(); - } - - // Looks for the first button reference that it can resolve - // and execute its command. - // NOTE: It is assumed that all buttons associated with a particular - // key have the same command - template - void RunFirstEnabledButtonCommand(const T& buttons) - { - auto buttonIterator = buttons.first; - for (; buttonIterator != buttons.second; ++buttonIterator) - { - auto button = buttonIterator->second.Resolve(); - if (button && button->IsEnabled) - { - RunButtonCommand(button); - break; - } - } - } - - void RunButtonCommand(ButtonBase ^ button) - { - if (button->IsEnabled) - { - auto command = button->Command; - auto parameter = button->CommandParameter; - if (command && command->CanExecute(parameter)) - { - command->Execute(parameter); - } - - auto radio = dynamic_cast(button); - if (radio) - { - radio->IsChecked = true; - return; - } - - auto toggle = dynamic_cast(button); - if (toggle) - { - toggle->IsChecked = !toggle->IsChecked->Value; - return; - } - } - } - } -} - -static multimap s_ignoreNextEscape; -static multimap s_keepIgnoringEscape; -static multimap s_fHonorShortcuts; -static multimap s_fHandledEnter; -static multimap s_AboutFlyout; - -void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - int viewId = Utils::GetWindowId(); - - if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end()) - { - s_ignoreNextEscape.erase(viewId); - s_ignoreNextEscape.insert(std::make_pair(viewId, true)); - } - - if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) - { - s_keepIgnoringEscape.erase(viewId); - s_keepIgnoringEscape.insert(std::make_pair(viewId, !onlyOnce)); - } -} - -void KeyboardShortcutManager::HonorEscape() -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - int viewId = Utils::GetWindowId(); - - if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end()) - { - s_ignoreNextEscape.erase(viewId); - s_ignoreNextEscape.insert(std::make_pair(viewId, false)); - } - - if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) - { - s_keepIgnoringEscape.erase(viewId); - s_keepIgnoringEscape.insert(std::make_pair(viewId, false)); - } -} - -void KeyboardShortcutManager::OnCharacterPropertyChanged(DependencyObject ^ target, String ^ oldValue, String ^ newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_CharacterForButtons.find(viewId); - - if (iterViewMap != s_CharacterForButtons.end()) - { - if (oldValue) - { - iterViewMap->second.erase(oldValue->Data()[0]); - } - - if (newValue) - { - if (newValue == L".") - { - wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator(); - iterViewMap->second.insert(std::make_pair(decSep, WeakReference(button))); - } - else - { - iterViewMap->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button))); - } - } - } - else - { - s_CharacterForButtons.insert(std::make_pair(viewId, std::multimap())); - - if (newValue == L".") - { - wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator(); - s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(decSep, WeakReference(button))); - } - else - { - s_CharacterForButtons.find(viewId)->second.insert(std::make_pair(newValue->Data()[0], WeakReference(button))); - } - } -} - -void KeyboardShortcutManager::OnVirtualKeyPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = static_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeysForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeysForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeysForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeysForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyControlChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - Control ^ control = dynamic_cast(target); - - if (control == nullptr) - { - // Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case - control = safe_cast(target); - } - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyControlChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyControlChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(control))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyControlChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(control))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyShiftChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyShiftChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyShiftChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyAltChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - MUXC::NavigationView ^ navView = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyAltChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyAltChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(navView))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyAltChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(navView))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyControlShiftChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyControlShiftChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyControlShiftChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyInverseChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyInverseChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyInverseChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button))); - } -} - -void KeyboardShortcutManager::OnVirtualKeyControlInverseChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto button = safe_cast(target); - - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId); - - // Check if the View Id has already been registered - if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end()) - { - iterViewMap->second.insert(std::make_pair(newValue, WeakReference(button))); - } - else - { - // If the View Id is not already registered, then register it and make the entry - s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(viewId, std::multimap())); - s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second.insert(std::make_pair(newValue, WeakReference(button))); - } -} - -// In the three event handlers below we will not mark the event as handled -// because this is a supplemental operation and we don't want to interfere with -// the normal keyboard handling. -void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow ^ sender, CharacterReceivedEventArgs ^ args) -{ - int viewId = Utils::GetWindowId(); - auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId); - - if (currentHonorShortcuts != s_fHonorShortcuts.end()) - { - if (currentHonorShortcuts->second) - { - wchar_t character = static_cast(args->KeyCode); - auto buttons = s_CharacterForButtons.find(viewId)->second.equal_range(character); - - RunFirstEnabledButtonCommand(buttons); - - LightUpButtons(buttons); - } - } -} - -const std::multimap& GetCurrentKeyDictionary(MyVirtualKey key, bool altPressed = false) -{ - int viewId = Utils::GetWindowId(); - - if (altPressed) - { - return s_VirtualKeyAltChordsForButtons.find(viewId)->second; - } - else if ( - (s_ShiftKeyPressed.find(viewId)->second) - && ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) - { - return s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second; - } - else if (s_ShiftKeyPressed.find(viewId)->second) - { - return s_VirtualKeyShiftChordsForButtons.find(viewId)->second; - } - else if (s_ShiftButtonChecked.find(viewId)->second) - { - if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) - { - auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId); - if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end()) - { - for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator) - { - if (key == iterator->first) - { - return s_VirtualKeyControlInverseChordsForButtons.find(viewId)->second; - } - } - } - } - else - { - auto iterViewMap = s_VirtualKeyControlInverseChordsForButtons.find(viewId); - if (iterViewMap != s_VirtualKeyControlInverseChordsForButtons.end()) - { - for (auto iterator = iterViewMap->second.begin(); iterator != iterViewMap->second.end(); ++iterator) - { - if (key == iterator->first) - { - return s_VirtualKeyInverseChordsForButtons.find(viewId)->second; - } - } - } - } - } - if ((Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) - { - return s_VirtualKeyControlChordsForButtons.find(viewId)->second; - } - else - { - return s_VirtualKeysForButtons.find(viewId)->second; - } -} - -void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs ^ args) -{ - // If keyboard shortcuts like Ctrl+C or Ctrl+V are not handled - if (!args->Handled) - { - auto key = args->VirtualKey; - int viewId = Utils::GetWindowId(); - - auto currentControlKeyPressed = s_ControlKeyPressed.find(viewId); - auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId); - - bool isControlKeyPressed = (currentControlKeyPressed != s_ControlKeyPressed.end()) && (currentControlKeyPressed->second); - bool isShiftKeyPressed = (currentShiftKeyPressed != s_ShiftKeyPressed.end()) && (currentShiftKeyPressed->second); - - // Handle Ctrl + E for DateCalculator - if ((key == VirtualKey::E) && isControlKeyPressed && !isShiftKeyPressed) - { - const auto& lookupMap = GetCurrentKeyDictionary(static_cast(key)); - auto buttons = lookupMap.equal_range(static_cast(key)); - auto navView = buttons.first->second.Resolve(); - auto appViewModel = safe_cast(navView->DataContext); - appViewModel->Mode = ViewMode::Date; - auto categoryName = AppResourceProvider::GetInstance()->GetResourceString(L"DateCalculationModeText"); - appViewModel->CategoryName = categoryName; - - auto menuItems = static_cast ^>(navView->MenuItemsSource); - auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date); - navView->SelectedItem = menuItems->GetAt(flatIndex); - return; - } - - auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId); - - auto currentIgnoreNextEscape = s_ignoreNextEscape.find(viewId); - - if (currentIgnoreNextEscape != s_ignoreNextEscape.end()) - { - if (currentIgnoreNextEscape->second && key == VirtualKey::Escape) - { - auto currentKeepIgnoringEscape = s_keepIgnoringEscape.find(viewId); - - if (currentKeepIgnoringEscape != s_keepIgnoringEscape.end()) - { - if (!currentKeepIgnoringEscape->second) - { - HonorEscape(); - } - return; - } - } - } - - if (key == VirtualKey::Control) - { - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto currControlKeyPressed = s_ControlKeyPressed.find(viewId); - - if (currControlKeyPressed != s_ControlKeyPressed.end()) - { - s_ControlKeyPressed.erase(viewId); - s_ControlKeyPressed.insert(std::make_pair(viewId, true)); - } - return; - } - else if (key == VirtualKey::Shift) - { - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto currShiftKeyPressed = s_ShiftKeyPressed.find(viewId); - - if (currShiftKeyPressed != s_ShiftKeyPressed.end()) - { - s_ShiftKeyPressed.erase(viewId); - s_ShiftKeyPressed.insert(std::make_pair(viewId, true)); - } - return; - } - - const auto& lookupMap = GetCurrentKeyDictionary(static_cast(key)); - auto buttons = lookupMap.equal_range(static_cast(key)); - - auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId); - - if (currentHonorShortcuts != s_fHonorShortcuts.end()) - { - if (currentHonorShortcuts->second) - { - RunFirstEnabledButtonCommand(buttons); - - // Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V - // is pressed. When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open. Ctrl+Insert is - // equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V - if (currentIsDropDownOpen != s_IsDropDownOpen.end() && !currentIsDropDownOpen->second) - { - // Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed - if (!(isControlKeyPressed && (key == VirtualKey::C || key == VirtualKey::V || key == VirtualKey::Insert)) - && !(isShiftKeyPressed && (key == VirtualKey::Insert))) - { - LightUpButtons(buttons); - } - } - } - } - } -} - -void KeyboardShortcutManager::OnKeyUpHandler(CoreWindow ^ sender, KeyEventArgs ^ args) -{ - int viewId = Utils::GetWindowId(); - auto key = args->VirtualKey; - - if (key == VirtualKey::Shift) - { - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto currentShiftKeyPressed = s_ShiftKeyPressed.find(viewId); - - if (currentShiftKeyPressed != s_ShiftKeyPressed.end()) - { - s_ShiftKeyPressed.erase(viewId); - s_ShiftKeyPressed.insert(std::make_pair(viewId, false)); - } - } - else if (key == VirtualKey::Control) - { - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - auto currControlKeyPressed = s_ControlKeyPressed.find(viewId); - - if (currControlKeyPressed != s_ControlKeyPressed.end()) - { - s_ControlKeyPressed.erase(viewId); - s_ControlKeyPressed.insert(std::make_pair(viewId, false)); - } - } -} - -void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher ^, AcceleratorKeyEventArgs ^ args) -{ - if (args->KeyStatus.IsKeyReleased) - { - auto key = args->VirtualKey; - bool altPressed = args->KeyStatus.IsMenuKeyDown; - - // If the Alt/Menu key is not pressed then we don't care about the key anymore - if (!altPressed) - { - return; - } - - const auto& lookupMap = GetCurrentKeyDictionary(static_cast(key), altPressed); - auto listItems = lookupMap.equal_range(static_cast(key)); - for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator) - { - auto item = listIterator->second.Resolve(); - if (item != nullptr) - { - auto navView = safe_cast(item); - - auto menuItems = static_cast ^>(navView->MenuItemsSource); - if (menuItems != nullptr) - { - auto vm = safe_cast(navView->DataContext); - if (nullptr != vm) - { - ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast(key)); - auto nvi = dynamic_cast(menuItems->GetAt(NavCategory::GetFlatIndex(toMode))); - if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode)) - { - vm->Mode = toMode; - navView->SelectedItem = nvi; - } - } - } - break; - } - } - } - - if (args->VirtualKey == VirtualKey::Escape) - { - int viewId = Utils::GetWindowId(); - auto iterViewMap = s_AboutFlyout.find(viewId); - - if ((iterViewMap != s_AboutFlyout.end()) && (iterViewMap->second != nullptr)) - { - iterViewMap->second->Hide(); - } - } -} - -void KeyboardShortcutManager::Initialize() -{ - auto coreWindow = Window::Current->CoreWindow; - coreWindow->CharacterReceived += - ref new TypedEventHandler(&KeyboardShortcutManager::OnCharacterReceivedHandler); - coreWindow->KeyDown += ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyDownHandler); - coreWindow->KeyUp += ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyUpHandler); - coreWindow->Dispatcher->AcceleratorKeyActivated += - ref new TypedEventHandler(&KeyboardShortcutManager::OnAcceleratorKeyActivated); - - KeyboardShortcutManager::RegisterNewAppViewId(); -} - -void KeyboardShortcutManager::ShiftButtonChecked(bool checked) -{ - int viewId = Utils::GetWindowId(); - - if (s_ShiftButtonChecked.find(viewId) != s_ShiftButtonChecked.end()) - { - s_ShiftButtonChecked.erase(viewId); - s_ShiftButtonChecked.insert(std::make_pair(viewId, checked)); - } -} - -void KeyboardShortcutManager::UpdateDropDownState(bool isOpen) -{ - int viewId = Utils::GetWindowId(); - - if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end()) - { - s_IsDropDownOpen.erase(viewId); - s_IsDropDownOpen.insert(std::make_pair(viewId, isOpen)); - } -} - -void KeyboardShortcutManager::UpdateDropDownState(Flyout ^ aboutPageFlyout) -{ - int viewId = Utils::GetWindowId(); - - if (s_AboutFlyout.find(viewId) != s_AboutFlyout.end()) - { - s_AboutFlyout.erase(viewId); - s_AboutFlyout.insert(std::make_pair(viewId, aboutPageFlyout)); - } -} - -void KeyboardShortcutManager::HonorShortcuts(bool allow) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - int viewId = Utils::GetWindowId(); - - if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end()) - { - s_fHonorShortcuts.erase(viewId); - s_fHonorShortcuts.insert(std::make_pair(viewId, allow)); - } -} - -void KeyboardShortcutManager::HandledEnter(bool ishandled) -{ - int viewId = Utils::GetWindowId(); - - if (s_fHandledEnter.find(viewId) != s_fHandledEnter.end()) - { - s_fHandledEnter.erase(viewId); - s_fHandledEnter.insert(std::make_pair(viewId, ishandled)); - } -} - -void KeyboardShortcutManager::RegisterNewAppViewId() -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - int appViewId = Utils::GetWindowId(); - - // Check if the View Id has already been registered - if (s_CharacterForButtons.find(appViewId) == s_CharacterForButtons.end()) - { - s_CharacterForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeysForButtons.find(appViewId) == s_VirtualKeysForButtons.end()) - { - s_VirtualKeysForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end()) - { - s_VirtualKeyControlChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end()) - { - s_VirtualKeyShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end()) - { - s_VirtualKeyAltChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end()) - { - s_VirtualKeyControlShiftChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyInverseChordsForButtons.find(appViewId) == s_VirtualKeyInverseChordsForButtons.end()) - { - s_VirtualKeyInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - if (s_VirtualKeyControlInverseChordsForButtons.find(appViewId) == s_VirtualKeyControlInverseChordsForButtons.end()) - { - s_VirtualKeyControlInverseChordsForButtons.insert(std::make_pair(appViewId, std::multimap())); - } - - s_ShiftKeyPressed.insert(std::make_pair(appViewId, false)); - s_ControlKeyPressed.insert(std::make_pair(appViewId, false)); - s_ShiftButtonChecked.insert(std::make_pair(appViewId, false)); - s_IsDropDownOpen.insert(std::make_pair(appViewId, false)); - s_ignoreNextEscape.insert(std::make_pair(appViewId, false)); - s_keepIgnoringEscape.insert(std::make_pair(appViewId, false)); - s_fHonorShortcuts.insert(std::make_pair(appViewId, true)); - s_fHandledEnter.insert(std::make_pair(appViewId, true)); - s_AboutFlyout.insert(std::make_pair(appViewId, nullptr)); -} - -void KeyboardShortcutManager::OnWindowClosed(int viewId) -{ - // Writer lock for the static maps - reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); - - s_CharacterForButtons.erase(viewId); - - s_VirtualKeysForButtons.erase(viewId); - s_VirtualKeyControlChordsForButtons.erase(viewId); - s_VirtualKeyShiftChordsForButtons.erase(viewId); - s_VirtualKeyAltChordsForButtons.erase(viewId); - s_VirtualKeyControlShiftChordsForButtons.erase(viewId); - s_VirtualKeyInverseChordsForButtons.erase(viewId); - s_VirtualKeyControlInverseChordsForButtons.erase(viewId); - - s_ShiftKeyPressed.erase(viewId); - s_ControlKeyPressed.erase(viewId); - s_ShiftButtonChecked.erase(viewId); - s_IsDropDownOpen.erase(viewId); - s_ignoreNextEscape.erase(viewId); - s_keepIgnoringEscape.erase(viewId); - s_fHonorShortcuts.erase(viewId); - s_fHandledEnter.erase(viewId); - s_AboutFlyout.erase(viewId); -} diff --git a/src/CalcViewModel/Common/LocalizationService.cpp b/src/CalcViewModel/Common/LocalizationService.cpp index 9060b04a..c7fb9e0c 100644 --- a/src/CalcViewModel/Common/LocalizationService.cpp +++ b/src/CalcViewModel/Common/LocalizationService.cpp @@ -545,7 +545,7 @@ unordered_map LocalizationService::GetTokenToReadableNameMap() static vector> s_noParenEngineKeyResourceMap = { // Programmer mode functions make_pair(L"9", L"LeftShift"), make_pair(L"10", L"RightShift"), - make_pair(L"LogBaseX", L"Logx"), + make_pair(L"LogBaseY", L"Logy"), // Y Root scientific function make_pair(L"16", L"YRoot") diff --git a/src/CalcViewModel/Common/LocalizationStringUtil.h b/src/CalcViewModel/Common/LocalizationStringUtil.h index 0e3d9dff..73da6d0d 100644 --- a/src/CalcViewModel/Common/LocalizationStringUtil.h +++ b/src/CalcViewModel/Common/LocalizationStringUtil.h @@ -81,6 +81,18 @@ namespace CalculatorApp { return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data(), param3->Data(), param4->Data()); } + + static Platform::String + ^ GetLocalizedString( + Platform::String ^ pMessage, + Platform::String ^ param1, + Platform::String ^ param2, + Platform::String ^ param3, + Platform::String ^ param4, + Platform::String ^ param5) + { + return LocalizationStringUtilInternal::GetLocalizedString(pMessage, param1->Data(), param2->Data(), param3->Data(), param4->Data(), param5->Data()); + } }; } } diff --git a/src/CalcViewModel/Common/NavCategory.cpp b/src/CalcViewModel/Common/NavCategory.cpp index 90f25c92..6af89959 100644 --- a/src/CalcViewModel/Common/NavCategory.cpp +++ b/src/CalcViewModel/Common/NavCategory.cpp @@ -5,13 +5,17 @@ #include "NavCategory.h" #include "AppResourceProvider.h" #include "Common/LocalizationStringUtil.h" +#include using namespace CalculatorApp; using namespace CalculatorApp::Common; +using namespace Concurrency; using namespace Platform; using namespace Platform::Collections; using namespace std; using namespace Windows::Foundation::Collections; +using namespace Windows::Management::Policies; +using namespace Windows::System; namespace UCM = UnitConversionManager; @@ -22,12 +26,6 @@ static constexpr bool SUPPORTS_ALL = true; static constexpr bool SUPPORTS_NEGATIVE = true; static constexpr bool POSITIVE_ONLY = false; -// The order of items in this list determines the order of groups in the menu. -static constexpr array s_categoryGroupManifest = { - NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" }, - NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" } -}; - // vvv THESE CONSTANTS SHOULD NEVER CHANGE vvv static constexpr int STANDARD_ID = 0; static constexpr int SCIENTIFIC_ID = 1; @@ -46,145 +44,237 @@ static constexpr int DATA_ID = 13; static constexpr int PRESSURE_ID = 14; static constexpr int ANGLE_ID = 15; static constexpr int CURRENCY_ID = 16; +static constexpr int GRAPHING_ID = 17; // ^^^ THESE CONSTANTS SHOULD NEVER CHANGE ^^^ +wchar_t* towchar_t(int number) +{ + auto wstr = to_wstring(number); + return _wcsdup(wstr.c_str()); +} + +bool IsGraphingModeAvailable() +{ + static bool supportGraph = Windows::Foundation::Metadata::ApiInformation::IsMethodPresent("Windows.UI.Text.RichEditTextDocument", "GetMath"); + return supportGraph; +} + +Box ^ _isGraphingModeEnabledCached = nullptr; +bool IsGraphingModeEnabled() +{ + if (!IsGraphingModeAvailable()) + { + return false; + } + + if (_isGraphingModeEnabledCached != nullptr) + { + return _isGraphingModeEnabledCached->Value; + } + + User ^ firstUser; + create_task(User::FindAllAsync(UserType::LocalUser)).then([&firstUser](IVectorView ^ users) { + firstUser = users->GetAt(0); }).wait(); + auto namedPolicyData = NamedPolicy::GetPolicyFromPathForUser(firstUser, L"Education", L"AllowGraphingCalculator"); + _isGraphingModeEnabledCached = namedPolicyData->GetBoolean() == true; + + return _isGraphingModeEnabledCached->Value; +} + // The order of items in this list determines the order of items in the menu. -static 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::Currency, - CURRENCY_ID, - L"Currency", - L"CategoryName_Currency", - L"\uEB0D", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Volume, - VOLUME_ID, - L"Volume", - L"CategoryName_Volume", - L"\uF1AA", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Length, - LENGTH_ID, - L"Length", - L"CategoryName_Length", - L"\uECC6", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Weight, - WEIGHT_ID, - L"Weight and Mass", - L"CategoryName_Weight", - L"\uF4C1", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Temperature, - TEMPERATURE_ID, - L"Temperature", - L"CategoryName_Temperature", - L"\uE7A3", - CategoryGroupType::Converter, - MyVirtualKey::None, - SUPPORTS_NEGATIVE }, - NavCategoryInitializer{ ViewMode::Energy, - ENERGY_ID, - L"Energy", - L"CategoryName_Energy", - L"\uECAD", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Area, - AREA_ID, - L"Area", - L"CategoryName_Area", - L"\uE809", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Speed, - SPEED_ID, - L"Speed", - L"CategoryName_Speed", - L"\uEADA", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Time, - TIME_ID, - L"Time", - L"CategoryName_Time", - L"\uE917", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Power, - POWER_ID, - L"Power", - L"CategoryName_Power", - L"\uE945", - CategoryGroupType::Converter, - MyVirtualKey::None, - SUPPORTS_NEGATIVE }, - NavCategoryInitializer{ ViewMode::Data, - DATA_ID, - L"Data", - L"CategoryName_Data", - L"\uF20F", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Pressure, - PRESSURE_ID, - L"Pressure", - L"CategoryName_Pressure", - L"\uEC4A", - CategoryGroupType::Converter, - MyVirtualKey::None, - POSITIVE_ONLY }, - NavCategoryInitializer{ ViewMode::Angle, - ANGLE_ID, - L"Angle", - L"CategoryName_Angle", - L"\uF515", - CategoryGroupType::Converter, - MyVirtualKey::None, - SUPPORTS_NEGATIVE } }; +static const list s_categoryManifest = [] { + auto res = list{ NavCategoryInitializer{ ViewMode::Standard, + STANDARD_ID, + L"Standard", + L"StandardMode", + L"\uE8EF", + CategoryGroupType::Calculator, + MyVirtualKey::Number1, + L"1", + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Scientific, + SCIENTIFIC_ID, + L"Scientific", + L"ScientificMode", + L"\uF196", + CategoryGroupType::Calculator, + MyVirtualKey::Number2, + L"2", + SUPPORTS_ALL, + true } }; + + int currentIndex = 3; + bool supportGraphingCalculator = IsGraphingModeAvailable(); + if (supportGraphingCalculator) + { + const bool isEnabled = IsGraphingModeEnabled(); + res.push_back(NavCategoryInitializer{ ViewMode::Graphing, + GRAPHING_ID, + L"Graphing", + L"GraphingCalculatorMode", + L"\uF770", + CategoryGroupType::Calculator, + MyVirtualKey::Number3, + L"3", + SUPPORTS_ALL, + isEnabled }); + ++currentIndex; + } + res.insert( + res.end(), + { NavCategoryInitializer{ ViewMode::Programmer, + PROGRAMMER_ID, + L"Programmer", + L"ProgrammerMode", + L"\uECCE", + CategoryGroupType::Calculator, + supportGraphingCalculator ? MyVirtualKey::Number4 : MyVirtualKey::Number3, + towchar_t(currentIndex++), + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Date, + DATE_ID, + L"Date", + L"DateCalculationMode", + L"\uE787", + CategoryGroupType::Calculator, + supportGraphingCalculator ? MyVirtualKey::Number5 : MyVirtualKey::Number4, + towchar_t(currentIndex++), + SUPPORTS_ALL, + true }, + NavCategoryInitializer{ ViewMode::Currency, + CURRENCY_ID, + L"Currency", + L"CategoryName_Currency", + L"\uEB0D", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Volume, + VOLUME_ID, + L"Volume", + L"CategoryName_Volume", + L"\uF1AA", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Length, + LENGTH_ID, + L"Length", + L"CategoryName_Length", + L"\uECC6", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Weight, + WEIGHT_ID, + L"Weight and Mass", + L"CategoryName_Weight", + L"\uF4C1", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Temperature, + TEMPERATURE_ID, + L"Temperature", + L"CategoryName_Temperature", + L"\uE7A3", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true }, + NavCategoryInitializer{ ViewMode::Energy, + ENERGY_ID, + L"Energy", + L"CategoryName_Energy", + L"\uECAD", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Area, + AREA_ID, + L"Area", + L"CategoryName_Area", + L"\uE809", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Speed, + SPEED_ID, + L"Speed", + L"CategoryName_Speed", + L"\uEADA", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Time, + TIME_ID, + L"Time", + L"CategoryName_Time", + L"\uE917", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Power, + POWER_ID, + L"Power", + L"CategoryName_Power", + L"\uE945", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true }, + NavCategoryInitializer{ ViewMode::Data, + DATA_ID, + L"Data", + L"CategoryName_Data", + L"\uF20F", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Pressure, + PRESSURE_ID, + L"Pressure", + L"CategoryName_Pressure", + L"\uEC4A", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + POSITIVE_ONLY, + true }, + NavCategoryInitializer{ ViewMode::Angle, + ANGLE_ID, + L"Angle", + L"CategoryName_Angle", + L"\uF515", + CategoryGroupType::Converter, + MyVirtualKey::None, + nullptr, + SUPPORTS_NEGATIVE, + true } }); + return res; +}(); // This function should only be used when storing the mode to app data. int NavCategory::Serialize(ViewMode mode) @@ -211,6 +301,14 @@ ViewMode NavCategory::Deserialize(Platform::Object ^ obj) if (iter != s_categoryManifest.end()) { + if (iter->viewMode == ViewMode::Graphing) + { + // check if the user is allowed to use this feature + if (!IsGraphingModeEnabled()) + { + return ViewMode::None; + } + } return iter->viewMode; } } @@ -226,11 +324,23 @@ bool NavCategory::IsValidViewMode(ViewMode mode) return iter != s_categoryManifest.end(); } +bool NavCategory::IsViewModeEnabled(ViewMode mode) +{ + auto iter = + find_if(begin(s_categoryManifest), end(s_categoryManifest), [mode](const NavCategoryInitializer& initializer) { return initializer.viewMode == mode && initializer.isEnabled; }); + + return iter != s_categoryManifest.end(); +} + bool NavCategory::IsCalculatorViewMode(ViewMode mode) { - // Historically, Date Calculator is not a Calculator mode - // even though it is in the Calculator category. - return !IsDateCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator); + // Historically, Calculator modes are Standard, Scientific, and Programmer. + return !IsDateCalculatorViewMode(mode) && !IsGraphingCalculatorViewMode(mode) && IsModeInCategoryGroup(mode, CategoryGroupType::Calculator); +} + +bool NavCategory::IsGraphingCalculatorViewMode(ViewMode mode) +{ + return mode == ViewMode::Graphing; } bool NavCategory::IsDateCalculatorViewMode(ViewMode mode) @@ -389,10 +499,12 @@ NavCategoryGroup::NavCategoryGroup(const NavCategoryGroupInitializer& groupIniti categoryName, categoryAutomationName, StringReference(categoryInitializer.glyph), - resProvider->GetResourceString(nameResourceKey + "AccessKey"), + categoryInitializer.accessKey != nullptr ? ref new String(categoryInitializer.accessKey) + : resProvider->GetResourceString(nameResourceKey + "AccessKey"), groupMode, categoryInitializer.viewMode, - categoryInitializer.supportsNegative)); + categoryInitializer.supportsNegative, + categoryInitializer.isEnabled)); } } } @@ -407,29 +519,12 @@ IObservableVector ^ NavCategoryGroup::CreateMenuOptions() NavCategoryGroup ^ NavCategoryGroup::CreateCalculatorCategory() { - return ref new NavCategoryGroup(s_categoryGroupManifest.at(0)); + return ref new NavCategoryGroup( + NavCategoryGroupInitializer{ CategoryGroupType::Calculator, L"CalculatorModeTextCaps", L"CalculatorModeText", L"CalculatorModePluralText" }); } NavCategoryGroup ^ NavCategoryGroup::CreateConverterCategory() { - return ref new NavCategoryGroup(s_categoryGroupManifest.at(1)); -} - -vector NavCategoryGroup::GetInitializerCategoryGroup(CategoryGroupType groupType) -{ - vector initializers{}; - copy_if(begin(s_categoryManifest), end(s_categoryManifest), back_inserter(initializers), [groupType](const NavCategoryInitializer& initializer) { - return initializer.groupType == groupType; - }); - - return initializers; -} - -String ^ NavCategoryGroup::GetHeaderResourceKey(CategoryGroupType type) -{ - auto iter = find_if(begin(s_categoryGroupManifest), end(s_categoryGroupManifest), [type](const NavCategoryGroupInitializer& initializer) { - return initializer.type == type; - }); - - return (iter != s_categoryGroupManifest.end()) ? StringReference(iter->headerResourceKey) : nullptr; + return ref new NavCategoryGroup( + NavCategoryGroupInitializer{ CategoryGroupType::Converter, L"ConverterModeTextCaps", L"ConverterModeText", L"ConverterModePluralText" }); } diff --git a/src/CalcViewModel/Common/NavCategory.h b/src/CalcViewModel/Common/NavCategory.h index b1cf1d3d..b7c46057 100644 --- a/src/CalcViewModel/Common/NavCategory.h +++ b/src/CalcViewModel/Common/NavCategory.h @@ -44,7 +44,8 @@ namespace CalculatorApp Data = 13, Pressure = 14, Angle = 15, - Currency = 16 + Currency = 16, + Graphing = 17 }; public @@ -66,7 +67,9 @@ namespace CalculatorApp wchar_t const* glyph, CategoryGroupType group, MyVirtualKey vKey, - bool categorySupportsNegative) + wchar_t const* aKey, + bool categorySupportsNegative, + bool enabled) : viewMode(mode) , serializationId(id) , friendlyName(name) @@ -74,7 +77,9 @@ namespace CalculatorApp , glyph(glyph) , groupType(group) , virtualKey(vKey) + , accessKey(aKey) , supportsNegative(categorySupportsNegative) + , isEnabled(enabled) { } @@ -85,7 +90,9 @@ namespace CalculatorApp const wchar_t* const glyph; const CategoryGroupType groupType; const MyVirtualKey virtualKey; + const wchar_t* const accessKey; const bool supportsNegative; + const bool isEnabled; }; private @@ -109,45 +116,17 @@ namespace CalculatorApp { public: OBSERVABLE_OBJECT(); + PROPERTY_R(Platform::String ^, Name); + PROPERTY_R(Platform::String ^, AutomationName); + PROPERTY_R(Platform::String ^, Glyph); + PROPERTY_R(ViewMode, Mode); + PROPERTY_R(Platform::String ^, AccessKey); + PROPERTY_R(bool, SupportsNegative); + PROPERTY_R(bool, IsEnabled); property Platform::String - ^ Name { Platform::String ^ get() { return m_name; } } + ^ AutomationId { Platform::String ^ get() { return m_Mode.ToString(); } } - property Platform::String - ^ AutomationName { Platform::String ^ get() { return m_automationName; } } - - property Platform::String - ^ Glyph { Platform::String ^ get() { return m_glyph; } } - - property int Position - { - int get() - { - return m_position; - } - } - - property ViewMode Mode - { - ViewMode get() - { - return m_viewMode; - } - } - - property Platform::String - ^ AutomationId { Platform::String ^ get() { return m_viewMode.ToString(); } } - - property Platform::String - ^ AccessKey { Platform::String ^ get() { return m_accessKey; } } - - property bool SupportsNegative - { - bool get() - { - return m_supportsNegative; - } - } // For saving/restoring last mode used. static int Serialize(ViewMode mode); @@ -155,7 +134,9 @@ namespace CalculatorApp static ViewMode GetViewModeForFriendlyName(Platform::String ^ name); static bool IsValidViewMode(ViewMode mode); + static bool IsViewModeEnabled(ViewMode mode); static bool IsCalculatorViewMode(ViewMode mode); + static bool IsGraphingCalculatorViewMode(ViewMode mode); static bool IsDateCalculatorViewMode(ViewMode mode); static bool IsConverterViewMode(ViewMode mode); @@ -178,16 +159,17 @@ namespace CalculatorApp Platform::String ^ accessKey, Platform::String ^ mode, ViewMode viewMode, - bool supportsNegative) - : m_name(name) - , m_automationName(automationName) - , m_glyph(glyph) - , m_accessKey(accessKey) - , m_mode(mode) - , m_viewMode(viewMode) - , m_supportsNegative(supportsNegative) + bool supportsNegative, + bool isEnabled) + : m_Name(name) + , m_AutomationName(automationName) + , m_Glyph(glyph) + , m_AccessKey(accessKey) + , m_modeString(mode) + , m_Mode(viewMode) + , m_SupportsNegative(supportsNegative) + , m_IsEnabled(isEnabled) { - m_position = NavCategory::GetPosition(m_viewMode); } static std::vector GetCategoryAcceleratorKeys(); @@ -195,14 +177,7 @@ namespace CalculatorApp private: static bool IsModeInCategoryGroup(ViewMode mode, CategoryGroupType groupType); - ViewMode m_viewMode; - Platform::String ^ m_name; - Platform::String ^ m_automationName; - Platform::String ^ m_glyph; - Platform::String ^ m_accessKey; - Platform::String ^ m_mode; - int m_position; - bool m_supportsNegative; + Platform::String ^ m_modeString; }; [Windows::UI::Xaml::Data::Bindable] public ref class NavCategoryGroup sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged @@ -216,15 +191,11 @@ namespace CalculatorApp static Windows::Foundation::Collections::IObservableVector ^ CreateMenuOptions(); - static Platform::String ^ GetHeaderResourceKey(CategoryGroupType type); - internal : static NavCategoryGroup ^ CreateCalculatorCategory(); static NavCategoryGroup ^ CreateConverterCategory(); private: NavCategoryGroup(const NavCategoryGroupInitializer& groupInitializer); - - static std::vector GetInitializerCategoryGroup(CategoryGroupType groupType); }; } } diff --git a/src/CalcViewModel/Common/TraceActivity.h b/src/CalcViewModel/Common/TraceActivity.h deleted file mode 100644 index 41046a63..00000000 --- a/src/CalcViewModel/Common/TraceActivity.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - constexpr int64_t WINEVENT_KEYWORD_RESPONSE_TIME = 0x1000000000000; - - // RAII wrapper that automatically sends the Stop event when the class gets destructed. - class TraceActivity - { - public: - TraceActivity() - : m_channel(nullptr) - , m_activity(nullptr) - , m_fields(nullptr) - { - } - - TraceActivity( - winrt::Windows::Foundation::Diagnostics::LoggingChannel channel, - std::wstring_view activityName, - winrt::Windows::Foundation::Diagnostics::LoggingFields fields) - : m_channel(channel) - , m_activityName(activityName) - , m_fields(fields) - , m_activity(nullptr) - { - // Write the activity's START event. Note that you must not specify keyword - // or level for START and STOP events because they always use the activity's - // keyword and level. - m_activity = m_channel.StartActivity( - m_activityName, - m_fields, - winrt::Windows::Foundation::Diagnostics::LoggingLevel::Verbose, - winrt::Windows::Foundation::Diagnostics::LoggingOptions(WINEVENT_KEYWORD_RESPONSE_TIME)); - } - - ~TraceActivity() - { - if (m_activity != nullptr) - { - // Write the activity's STOP event. - m_activity.StopActivity(m_activityName, m_fields); - m_activity = nullptr; - } - } - - private: - std::wstring m_activityName; - winrt::Windows::Foundation::Diagnostics::LoggingChannel m_channel; - winrt::Windows::Foundation::Diagnostics::LoggingFields m_fields; - winrt::Windows::Foundation::Diagnostics::LoggingActivity m_activity; - }; -} diff --git a/src/CalcViewModel/Common/TraceLogger.cpp b/src/CalcViewModel/Common/TraceLogger.cpp index 845b010b..f58513de 100644 --- a/src/CalcViewModel/Common/TraceLogger.cpp +++ b/src/CalcViewModel/Common/TraceLogger.cpp @@ -8,15 +8,15 @@ using namespace CalculatorApp; using namespace CalculatorApp::Common; +using namespace TraceLogging; using namespace Concurrency; using namespace std; using namespace Platform; -using namespace winrt; -using namespace winrt::Windows::Foundation; -using namespace winrt::Windows::Foundation::Diagnostics; -using namespace winrt::Windows::Globalization; -using namespace winrt::Windows::Globalization::DateTimeFormatting; -using namespace winrt::Windows::System::UserProfile; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Diagnostics; +using namespace Windows::Globalization; +using namespace Windows::Globalization::DateTimeFormatting; +using namespace Windows::System::UserProfile; namespace CalculatorApp { @@ -35,36 +35,23 @@ namespace CalculatorApp 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_SHOW_HIDE_BUTTON_CLICKED = L"ShowHideButtonClicked"; + constexpr auto EVENT_NAME_GRAPH_BUTTON_CLICKED = L"GraphButtonClicked"; + constexpr auto EVENT_NAME_GRAPH_LINE_STYLE_CHANGED = L"GraphLineStyleChanged"; + constexpr auto EVENT_NAME_VARIABLE_CHANGED = L"VariableChanged"; + constexpr auto EVENT_NAME_VARIABLE_SETTING_CHANGED = L"VariableSettingChanged"; + constexpr auto EVENT_NAME_GRAPH_SETTINGS_CHANGED = L"GraphSettingsChanged"; + constexpr auto EVENT_NAME_GRAPH_THEME = L"GraphTheme"; 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_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_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 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 + constexpr auto CALC_MODE = L"CalcMode"; + constexpr auto GRAPHING_MODE = L"Graphing"; #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 }), - GUID{ 0x905ca09, 0x610e, 0x401e, 0xb6, 0x50, 0x2f, 0x21, 0x29, 0x80, 0xb9, 0xe0 }) - , // Unique providerID {0905CA09-610E-401E-B650-2F212980B9E0} - m_appLaunchActivity{ nullptr } { - CoCreateGuid(&sessionGuid); } TraceLogger ^ TraceLogger::GetInstance() @@ -73,33 +60,6 @@ namespace CalculatorApp return s_selfInstance; } - bool TraceLogger::GetTraceLoggingProviderEnabled() - { - return g_calculatorProvider.Enabled(); - } - -#pragma region Tracing methods - void TraceLogger::LogLevel1Event(wstring_view eventName, LoggingFields fields) - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_1)); - } - - void TraceLogger::LogLevel2Event(wstring_view eventName, LoggingFields fields) - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_2)); - } - - void TraceLogger::LogLevel3Event(wstring_view eventName, LoggingFields fields) - { - g_calculatorProvider.LogEvent(eventName, fields, LoggingLevel::Verbose, LoggingOptions(MICROSOFT_KEYWORD_LEVEL_3)); - } - - unique_ptr TraceLogger::CreateTraceActivity(wstring_view eventName, LoggingFields fields) - { - return make_unique(g_calculatorProvider, eventName, fields); - } -#pragma endregion - // return true if windowId is logged once else return false bool TraceLogger::IsWindowIdInLog(int windowId) { @@ -116,18 +76,12 @@ namespace CalculatorApp void TraceLogger::LogVisualStateChanged(ViewMode mode, String ^ state, bool isAlwaysOnTop) { - if (!GetTraceLoggingProviderEnabled()) - { - return; - } + auto fields = ref new LoggingFields(); - LoggingFields fields{}; - fields.AddGuid(L"SessionGuid", sessionGuid); - fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddString(L"VisualState", state->Data()); - fields.AddBoolean(L"IsAlwaysOnTop", isAlwaysOnTop); - fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogLevel2Event(EVENT_NAME_VISUAL_STATE_CHANGED, fields); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(L"VisualState"), state); + fields->AddBoolean(StringReference(L"IsAlwaysOnTop"), isAlwaysOnTop); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VISUAL_STATE_CHANGED), fields); } void TraceLogger::LogWindowCreated(ViewMode mode, int windowId) @@ -138,122 +92,69 @@ namespace CalculatorApp windowIdLog.push_back(windowId); } - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields 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); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddUInt64(StringReference(L"NumOfOpenWindows"), currentWindowCount); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_WINDOW_ON_CREATED), fields); } void TraceLogger::LogModeChange(ViewMode mode) { - if (!GetTraceLoggingProviderEnabled()) - return; - if (NavCategory::IsValidViewMode(mode)) { - LoggingFields 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); + auto fields = ref new LoggingFields(); + ; + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MODE_CHANGED), fields); } } void TraceLogger::LogHistoryItemLoad(ViewMode mode, int historyListSize, int loadedIndex) { - if (!GetTraceLoggingProviderEnabled()) - { - return; - } - - LoggingFields 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); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddInt32(StringReference(L"HistoryListSize"), historyListSize); + fields->AddInt32(StringReference(L"HistoryItemIndex"), loadedIndex); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_HISTORY_ITEM_LOAD), fields); } void TraceLogger::LogMemoryItemLoad(ViewMode mode, int memoryListSize, int loadedIndex) { - if (!GetTraceLoggingProviderEnabled()) - { - return; - } - - LoggingFields fields{}; - 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); - LogLevel2Event(EVENT_NAME_MEMORY_ITEM_LOAD, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddInt32(StringReference(L"MemoryListSize"), memoryListSize); + fields->AddInt32(StringReference(L"MemoryItemIndex"), loadedIndex); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_MEMORY_ITEM_LOAD), fields); } void TraceLogger::LogError(ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString) { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddGuid(L"SessionGuid", sessionGuid); - fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddString(L"FunctionName", functionName->Data()); - fields.AddString(L"Message", errorString->Data()); - fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogLevel2Event(EVENT_NAME_EXCEPTION, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(L"FunctionName"), functionName); + fields->AddString(StringReference(L"Message"), errorString); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); } void TraceLogger::LogStandardException(ViewMode mode, wstring_view functionName, const exception& e) { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddGuid(L"SessionGuid", sessionGuid); - fields.AddString(L"CalcMode", NavCategory::GetFriendlyName(mode)->Data()); - fields.AddString(L"FunctionName", functionName); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(L"FunctionName"), StringReference(functionName.data())); wstringstream exceptionMessage; exceptionMessage << e.what(); - 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(ViewMode mode, wstring_view functionName, hresult_error const& e) - { - 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.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogLevel2Event(EVENT_NAME_EXCEPTION, fields); + fields->AddString(StringReference(L"Message"), StringReference(exceptionMessage.str().data())); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); } void TraceLogger::LogPlatformException(ViewMode mode, wstring_view functionName, Platform::Exception ^ e) { - 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.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogLevel2Event(EVENT_NAME_EXCEPTION, fields); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + fields->AddString(StringReference(L"FunctionName"), StringReference(functionName.data())); + fields->AddString(StringReference(L"Message"), e->Message); + fields->AddInt32(StringReference(L"HRESULT"), e->HResult); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_EXCEPTION), fields); } void TraceLogger::UpdateButtonUsage(NumbersAndOperatorsEnum button, ViewMode mode) @@ -305,9 +206,6 @@ namespace CalculatorApp void TraceLogger::LogButtonUsage() { - if (!GetTraceLoggingProviderEnabled()) - return; - // Writer lock for the buttonLog resource reader_writer_lock::scoped_lock lock(s_traceLoggerLock); @@ -330,11 +228,9 @@ namespace CalculatorApp } } - 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); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(L"ButtonUsage"), buttonUsageString); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_BUTTON_USAGE), fields); buttonLog.clear(); } @@ -342,46 +238,89 @@ namespace CalculatorApp void TraceLogger::LogDateCalculationModeUsed(bool AddSubtractMode) { const wchar_t* calculationType = AddSubtractMode ? L"AddSubtractMode" : L"DateDifferenceMode"; - LoggingFields 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); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(ViewMode::Date)); + fields->AddString(StringReference(L"CalculationType"), StringReference(calculationType)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_DATE_CALCULATION_MODE_USED), fields); } void TraceLogger::LogConverterInputReceived(ViewMode mode) { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields 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); + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_CONVERTER_INPUT_RECEIVED), fields); } void TraceLogger::LogNavBarOpened() { - if (!GetTraceLoggingProviderEnabled()) - return; - - LoggingFields fields{}; - fields.AddGuid(L"SessionGuid", sessionGuid); - fields.AddUInt64(PDT_PRIVACY_DATA_TAG, PDT_PRODUCT_AND_SERVICE_USAGE); - LogLevel2Event(EVENT_NAME_NAV_BAR_OPENED, fields); + auto fields = ref new LoggingFields(); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_NAV_BAR_OPENED), fields); } void TraceLogger::LogInputPasted(ViewMode mode) { - if (!GetTraceLoggingProviderEnabled()) - return; + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), NavCategory::GetFriendlyName(mode)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_INPUT_PASTED), fields); + } - LoggingFields 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); + void TraceLogger::LogShowHideButtonClicked(bool isHideButton) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddBoolean(StringReference(L"IsHideButton"), isHideButton); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_SHOW_HIDE_BUTTON_CLICKED), fields); + } + + void TraceLogger::LogGraphButtonClicked(GraphButton buttonName, GraphButtonValue buttonValue) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(StringReference(L"ButtonName"), static_cast(buttonName)); + fields->AddInt16(StringReference(L"ButtonValue"), static_cast(buttonValue)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_BUTTON_CLICKED), fields); + } + + void TraceLogger::LogGraphLineStyleChanged(LineStyleType style) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(StringReference(L"StyleType"), static_cast(style)); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_LINE_STYLE_CHANGED), fields); + } + + void TraceLogger::LogVariableChanged(String ^ inputChangedType, String ^ variableName) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddString(StringReference(L"InputChangedType"), inputChangedType); + fields->AddString(StringReference(L"VariableName"), variableName); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VARIABLE_CHANGED), fields); + } + void TraceLogger::LogVariableSettingsChanged(String ^ setting) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddString(StringReference(L"SettingChanged"), setting); + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_VARIABLE_SETTING_CHANGED), fields); + } + + void TraceLogger::LogGraphSettingsChanged(GraphSettingsType settingType, String ^ settingValue) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddInt16(L"SettingType", static_cast(settingType)); + fields->AddString(L"SettingValue", settingValue); + + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_SETTINGS_CHANGED), fields); + } + + void TraceLogger::LogGraphTheme(String ^ graphTheme) + { + auto fields = ref new LoggingFields(); + fields->AddString(StringReference(CALC_MODE), StringReference(GRAPHING_MODE)); + fields->AddString(L"GraphTheme", graphTheme); + + TraceLoggingCommon::GetInstance()->LogLevel2Event(StringReference(EVENT_NAME_GRAPH_THEME), fields); } } diff --git a/src/CalcViewModel/Common/TraceLogger.h b/src/CalcViewModel/Common/TraceLogger.h index fa24f8e0..cef592e8 100644 --- a/src/CalcViewModel/Common/TraceLogger.h +++ b/src/CalcViewModel/Common/TraceLogger.h @@ -3,13 +3,9 @@ #pragma once -#include "CalcManager/Command.h" -#include "TraceActivity.h" #include "NavCategory.h" #include "CalculatorButtonUser.h" -static const int maxFunctionSize = (int)CalculationManager::Command::CommandBINEDITEND; - // A trace logging provider can only be instantiated and registered once per module. // This class implements a singleton model ensure that only one instance is created. namespace CalculatorApp @@ -28,12 +24,44 @@ namespace CalculatorApp } }; -public - ref class TraceLogger sealed + public enum class GraphSettingsType + { + Grid, + TrigUnits, + Theme + }; + + public enum class GraphButton + { + StylePicker, + RemoveFunction, + ActiveTracingChecked, + ActiveTracingUnchecked, + GraphSettings, + Share, + ZoomIn, + ZoomOut, + GraphView + }; + + public enum class GraphButtonValue + { + None, + AutomaticBestFit, + ManualAdjustment + }; + + public enum class LineStyleType + { + Color, + Pattern + }; + + public ref class TraceLogger sealed { public: static TraceLogger ^ GetInstance(); - bool GetTraceLoggingProviderEnabled(); + void LogModeChange(CalculatorApp::Common::ViewMode mode); void LogHistoryItemLoad(CalculatorApp::Common::ViewMode mode, int historyListSize, int loadedIndex); void LogMemoryItemLoad(CalculatorApp::Common::ViewMode mode, int memoryListSize, int loadedIndex); @@ -48,9 +76,15 @@ public void LogConverterInputReceived(CalculatorApp::Common::ViewMode mode); void LogNavBarOpened(); void LogError(CalculatorApp::Common::ViewMode mode, Platform::String ^ functionName, Platform::String ^ errorString); - internal : + void LogShowHideButtonClicked(bool isHideButton); + void LogGraphButtonClicked(GraphButton buttonName, GraphButtonValue buttonValue); + void LogGraphLineStyleChanged(LineStyleType style); + void LogVariableChanged(Platform::String ^ inputChangedType, Platform::String ^ variableName); + void LogVariableSettingsChanged(Platform::String ^ setting); + void LogGraphSettingsChanged(GraphSettingsType settingsType, Platform::String ^ settingValue); + void LogGraphTheme(Platform::String ^ graphTheme); + internal: void LogStandardException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ const std::exception& e); - void LogWinRTException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ winrt::hresult_error const& e); void LogPlatformException(CalculatorApp::Common::ViewMode mode, std::wstring_view functionName, _In_ Platform::Exception ^ e); void LogInputPasted(CalculatorApp::Common::ViewMode mode); @@ -58,24 +92,8 @@ public // Create an instance of TraceLogger TraceLogger(); - // 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); - void LogLevel2Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields); - void LogLevel3Event(std::wstring_view eventName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields); - - std::unique_ptr CreateTraceActivity(std::wstring_view activityName, winrt::Windows::Foundation::Diagnostics::LoggingFields fields); - - winrt::Windows::Foundation::Diagnostics::LoggingChannel g_calculatorProvider; - std::vector buttonLog; std::vector windowIdLog; - - GUID sessionGuid; uint64 currentWindowCount = 0; - - winrt::Windows::Foundation::Diagnostics::LoggingActivity m_appLaunchActivity; }; } diff --git a/src/CalcViewModel/Common/Utils.cpp b/src/CalcViewModel/Common/Utils.cpp index 368aa472..6bcc527d 100644 --- a/src/CalcViewModel/Common/Utils.cpp +++ b/src/CalcViewModel/Common/Utils.cpp @@ -14,14 +14,17 @@ using namespace CalculatorApp; using namespace CalculatorApp::Common; using namespace concurrency; +using namespace Graphing::Renderer; using namespace Platform; using namespace std; using namespace Utils; using namespace Windows::ApplicationModel::Resources; using namespace Windows::Storage::Streams; +using namespace Windows::UI; using namespace Windows::UI::Core; using namespace Windows::UI::ViewManagement; using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Media; using namespace Windows::Foundation; using namespace Windows::Storage; @@ -34,17 +37,6 @@ void Utils::IFTPlatformException(HRESULT hr) } } -String ^ Utils::GetStringValue(String ^ input) -{ - // Remove first and last " characters - if (input->Length() >= 3) - { - wstring out(input->Begin() + 1, input->End() - 1); - return ref new String(out.c_str()); - } - return input; -} - double Utils::GetDoubleFromWstring(wstring input) { constexpr wchar_t unWantedChars[] = { L' ', L',', 8234, 8235, 8236, 8237 }; @@ -80,67 +72,6 @@ bool Utils::IsLastCharacterTarget(_In_ wstring const& input, _In_ wchar_t target return !input.empty() && input.back() == target; } -void Utils::SerializeCommandsAndTokens( - _In_ shared_ptr>> const& tokens, - _In_ shared_ptr>> const& commands, - DataWriter ^ writer) -{ - // Save the size of the commands vector - writer->WriteUInt32(static_cast(commands->size())); - - SerializeCommandVisitor cmdVisitor(writer); - for (const auto& exprCmd : *commands) - { - CalculationManager::CommandType commandType = exprCmd->GetCommandType(); - writer->WriteInt32(static_cast(commandType)); - exprCmd->Accept(cmdVisitor); - } - - writer->WriteUInt32(static_cast(tokens->size())); - - for (const auto& eachToken : *tokens) - { - auto stringData = ref new Platform::String(eachToken.first.c_str()); - auto intData = eachToken.second; - writer->WriteUInt32(writer->MeasureString(stringData)); - writer->WriteString(stringData); - writer->WriteInt32(intData); - } -} - -const shared_ptr>> Utils::DeserializeCommands(DataReader ^ reader) -{ - auto commandVector = make_shared>>(); - auto commandVectorSize = reader->ReadUInt32(); - - CommandDeserializer cmdDeserializer(reader); - for (unsigned int i = 0; i < commandVectorSize; ++i) - { - auto commandTypeInt = reader->ReadInt32(); - CalculationManager::CommandType commandType = static_cast(commandTypeInt); - shared_ptr exprCmd = cmdDeserializer.Deserialize(commandType); - commandVector->push_back(exprCmd); - } - - return commandVector; -} - -const shared_ptr>> Utils::DeserializeTokens(DataReader ^ reader) -{ - auto tokenVector = make_shared>>(); - auto tokensSize = reader->ReadUInt32(); - - for (unsigned int i = 0; i < tokensSize; ++i) - { - auto stringDataLen = reader->ReadUInt32(); - auto stringData = reader->ReadString(stringDataLen); - auto intData = reader->ReadInt32(); - tokenVector->emplace_back(stringData->Data(), intData); - } - - return tokenVector; -} - DateTime Utils::GetUniversalSystemTime() { SYSTEMTIME sysTime = {}; @@ -196,3 +127,133 @@ task Utils::ReadFileFromFolder(IStorageFolder ^ folder, String ^ fileN String ^ contents = co_await FileIO::ReadTextAsync(file); co_return contents; } + +bool Utils::AreColorsEqual(const Color& color1, const Color& color2) +{ + return ((color1.A == color2.A) + && (color1.R == color2.R) + && (color1.G == color2.G) + && (color1.B == color2.B)); +} + +String^ Utils::Trim(String^ value) +{ + if (!value) + { + return nullptr; + } + + wstring trimmed = value->Data(); + Trim(trimmed); + return ref new String(trimmed.c_str()); +} + +void Utils::Trim(wstring& value) +{ + TrimFront(value); + TrimBack(value); +} + +void Utils::TrimFront(wstring& value) +{ + value.erase(value.begin(), find_if(value.cbegin(), value.cend(), [](int ch){ + return !isspace(ch); + })); +} + +void Utils::TrimBack(wstring& value) +{ + value.erase(find_if(value.crbegin(), value.crend(), [](int ch) { + return !isspace(ch); + }).base(), value.end()); +} + +String^ Utils::EscapeHtmlSpecialCharacters(String^ originalString, shared_ptr> specialCharacters) +{ + // Construct a default special characters if not provided. + if (specialCharacters == nullptr) + { + specialCharacters = make_shared>(); + specialCharacters->push_back(L'&'); + specialCharacters->push_back(L'\"'); + specialCharacters->push_back(L'\''); + specialCharacters->push_back(L'<'); + specialCharacters->push_back(L'>'); + } + + bool replaceCharacters = false; + const wchar_t* pCh; + String^ replacementString = nullptr; + + // First step is scanning the string for special characters. + // If there isn't any special character, we simply return the original string + for (pCh = originalString->Data(); *pCh; pCh++) + { + if (std::find(specialCharacters->begin(), specialCharacters->end(), *pCh) != specialCharacters->end()) + { + replaceCharacters = true; + break; + } + } + + if (replaceCharacters) + { + // If we indeed find a special character, we step back one character (the special + // character), and we create a new string where we replace those characters one by one + pCh--; + wstringstream buffer; + buffer << wstring(originalString->Data(), pCh); + + for (; *pCh; pCh++) + { + switch (*pCh) + { + case L'&': + buffer << L"&"; + break; + case L'\"': + buffer << L"""; + break; + case L'\'': + buffer << L"'"; + break; + case L'<': + buffer << L"<"; + break; + case L'>': + buffer << L">"; + break; + default: + buffer << *pCh; + } + } + replacementString = ref new String(buffer.str().c_str()); + } + + return replaceCharacters ? replacementString : originalString; +} + +bool operator==(const Color& color1, const Color& color2) +{ + return equal_to()(color1, color2); +} + +bool operator!=(const Color& color1, const Color& color2) +{ + return !(color1 == color2); +} + +// This method calculates the luminance ratio between White and the given background color. +// The luminance is calculate using the RGB values and does not use the A value. +// White or Black is returned +SolidColorBrush ^ Utils::GetContrastColor(Color backgroundColor) +{ + auto luminance = 0.2126 * backgroundColor.R + 0.7152 * backgroundColor.G + 0.0722 * backgroundColor.B; + + if ((255 + 0.05) / (luminance + 0.05) >= 2.5) + { + return static_cast(Application::Current->Resources->Lookup(L"WhiteBrush")); + } + + return static_cast(Application::Current->Resources->Lookup(L"BlackBrush")); +} diff --git a/src/CalcViewModel/Common/Utils.h b/src/CalcViewModel/Common/Utils.h index 2c752382..553e43f3 100644 --- a/src/CalcViewModel/Common/Utils.h +++ b/src/CalcViewModel/Common/Utils.h @@ -5,9 +5,13 @@ #include "CalcManager/ExpressionCommandInterface.h" #include "DelegateCommand.h" +#include "GraphingInterfaces/GraphingEnums.h" // Utility macros to make Models easier to write // generates a member variable called m_ + +#define SINGLE_ARG(...) __VA_ARGS__ + #define PROPERTY_R(t, n) \ property t n \ { \ @@ -94,7 +98,7 @@ public: #define OBSERVABLE_NAMED_PROPERTY_R(t, n) \ OBSERVABLE_PROPERTY_R(t, n) \ - internal: \ +public: \ static property Platform::String ^ n##PropertyName \ { \ Platform::String ^ get() { return Platform::StringReference(L#n); } \ @@ -104,7 +108,7 @@ public: #define OBSERVABLE_NAMED_PROPERTY_RW(t, n) \ OBSERVABLE_PROPERTY_RW(t, n) \ - internal: \ +public: \ static property Platform::String ^ n##PropertyName \ { \ Platform::String ^ get() { return Platform::StringReference(L#n); } \ @@ -237,9 +241,6 @@ namespace Utils }; } - const wchar_t LRE = 0x202a; // Left-to-Right Embedding - const wchar_t PDF = 0x202c; // Pop Directional Formatting - const wchar_t LRO = 0x202d; // Left-to-Right Override // Regular DependencyProperty template @@ -368,16 +369,7 @@ namespace Utils ref new Windows::UI::Xaml::PropertyChangedCallback(callback))); } - template - void Swap(T *ref1, T *ref2) - { - T temp = *ref1; - *ref1 = *ref2; - *ref2 = temp; - } - void IFTPlatformException(HRESULT hr); - Platform::String ^ GetStringValue(Platform::String ^ input); bool IsLastCharacterTarget(std::wstring const& input, wchar_t target); // Return wstring after removing characters specified by unwantedChars array @@ -394,23 +386,23 @@ namespace Utils double GetDoubleFromWstring(std::wstring input); int GetWindowId(); void RunOnUIThreadNonblocking(std::function&& function, _In_ Windows::UI::Core::CoreDispatcher ^ currentDispatcher); - void SerializeCommandsAndTokens( - _In_ std::shared_ptr>> const& tokens, - _In_ std::shared_ptr>> const& commands, - Windows::Storage::Streams::DataWriter ^ writer); - - const std::shared_ptr>> DeserializeCommands(Windows::Storage::Streams::DataReader ^ reader); - const std::shared_ptr>> DeserializeTokens(Windows::Storage::Streams::DataReader ^ reader); 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); + + Platform::String^ Trim(Platform::String^ value); + void Trim(std::wstring& value); + void TrimFront(std::wstring& value); + void TrimBack(std::wstring& value); + + Platform::String ^ EscapeHtmlSpecialCharacters(Platform::String ^ originalString, std::shared_ptr> specialCharacters = nullptr); + + Windows::UI::Xaml::Media::SolidColorBrush ^ GetContrastColor(Windows::UI::Color backgroundColor); } // This goes into the header to define the property, in the public: section of the class @@ -707,3 +699,18 @@ namespace CalculatorApp return to; } } + +// There's no standard definition of equality for Windows::UI::Color structs. +// Define a template specialization for std::equal_to. +template<> +class std::equal_to +{ +public: + bool operator()(const Windows::UI::Color& color1, const Windows::UI::Color& color2) + { + return Utils::AreColorsEqual(color1, color2); + } +}; + +bool operator==(const Windows::UI::Color& color1, const Windows::UI::Color& color2); +bool operator!=(const Windows::UI::Color& color1, const Windows::UI::Color& color2); diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp index d098885e..fc91a920 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.cpp @@ -220,14 +220,14 @@ void CurrencyDataLoader::LoadData() }; #pragma optimize("", on) -vector CurrencyDataLoader::LoadOrderedCategories() +vector CurrencyDataLoader::GetOrderedCategories() { // This function should not be called // The model will use the categories from UnitConverterDataLoader return vector(); } -vector CurrencyDataLoader::LoadOrderedUnits(const UCM::Category& /*category*/) +vector CurrencyDataLoader::GetOrderedUnits(const UCM::Category& /*category*/) { lock_guard lock(m_currencyUnitsMutex); return m_currencyUnits; diff --git a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h index d2548dcf..3216d63e 100644 --- a/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h +++ b/src/CalcViewModel/DataLoaders/CurrencyDataLoader.h @@ -63,8 +63,8 @@ namespace CalculatorApp // IConverterDataLoader void LoadData() override; - std::vector LoadOrderedCategories() override; - std::vector LoadOrderedUnits(const UCM::Category& category) override; + std::vector GetOrderedCategories() override; + std::vector GetOrderedUnits(const UCM::Category& category) override; std::unordered_map LoadOrderedRatios(const UCM::Unit& unit) override; bool SupportsCategory(const UnitConversionManager::Category& target) override; // IConverterDataLoader diff --git a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp index f90260d4..601b0112 100644 --- a/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp +++ b/src/CalcViewModel/DataLoaders/UnitConverterDataLoader.cpp @@ -22,18 +22,18 @@ UnitConverterDataLoader::UnitConverterDataLoader(GeographicRegion ^ region) : m_currentRegionCode(region->CodeTwoLetter) { m_categoryList = make_shared>(); - m_categoryToUnits = make_shared(); + m_categoryIDToUnitsMap = make_shared(); m_ratioMap = make_shared(); } -vector UnitConverterDataLoader::LoadOrderedCategories() +vector UnitConverterDataLoader::GetOrderedCategories() { return *m_categoryList; } -vector UnitConverterDataLoader::LoadOrderedUnits(const UCM::Category& category) +vector UnitConverterDataLoader::GetOrderedUnits(const UCM::Category& category) { - return m_categoryToUnits->at(category); + return this->m_categoryIDToUnitsMap->at(category.id); } unordered_map UnitConverterDataLoader::LoadOrderedRatios(const UCM::Unit& unit) @@ -75,8 +75,8 @@ void UnitConverterDataLoader::LoadData() GetConversionData(categoryToUnitConversionDataMap); GetExplicitConversionData(explicitConversionData); // This is needed for temperature conversions - m_categoryToUnits->clear(); - m_ratioMap->clear(); + this->m_categoryIDToUnitsMap->clear(); + this->m_ratioMap->clear(); for (UCM::Category objectCategory : *m_categoryList) { ViewMode categoryViewMode = NavCategory::Deserialize(objectCategory.id); @@ -86,7 +86,7 @@ void UnitConverterDataLoader::LoadData() // Currency is an ordered category but we do not want to process it here // because this function is not thread-safe and currency data is asynchronously // loaded. - m_categoryToUnits->insert(pair>(objectCategory, {})); + this->m_categoryIDToUnitsMap->insert(pair>(objectCategory.id, {})); continue; } @@ -103,7 +103,7 @@ void UnitConverterDataLoader::LoadData() } // Save units per category - m_categoryToUnits->insert(pair>(objectCategory, unitList)); + this->m_categoryIDToUnitsMap->insert(pair>(objectCategory.id, unitList)); // For each unit, populate the conversion data for (UCM::Unit unit : unitList) @@ -822,7 +822,7 @@ void UnitConverterDataLoader::GetConversionData(_In_ unordered_map LoadOrderedCategories() override; - std::vector LoadOrderedUnits(const UnitConversionManager::Category& c) override; + std::vector GetOrderedCategories() override; + std::vector GetOrderedUnits(const UnitConversionManager::Category& c) override; std::unordered_map LoadOrderedRatios(const UnitConversionManager::Unit& unit) override; bool SupportsCategory(const UnitConversionManager::Category& target) override; @@ -87,7 +87,7 @@ namespace CalculatorApp std::wstring GetLocalizedStringName(_In_ Platform::String ^ stringId); std::shared_ptr> m_categoryList; - std::shared_ptr m_categoryToUnits; + std::shared_ptr m_categoryIDToUnitsMap; std::shared_ptr m_ratioMap; Platform::String ^ m_currentRegionCode; }; diff --git a/src/CalcViewModel/DateCalculatorViewModel.cpp b/src/CalcViewModel/DateCalculatorViewModel.cpp index 20ee012b..a34c1288 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.cpp +++ b/src/CalcViewModel/DateCalculatorViewModel.cpp @@ -60,7 +60,7 @@ DateCalculatorViewModel::DateCalculatorViewModel() auto today = calendar->GetDateTime(); // FromDate and ToDate should be clipped (adjusted to a consistent hour in UTC) - m_fromDate = m_toDate = ClipTime(today, true); + m_fromDate = m_toDate = ClipTime(today); // StartDate should not be clipped m_startDate = today; @@ -83,7 +83,7 @@ DateCalculatorViewModel::DateCalculatorViewModel() DayOfWeek trueDayOfWeek = calendar->DayOfWeek; - DateTime clippedTime = ClipTime(today, false); + DateTime clippedTime = ClipTime(today); calendar->SetDateTime(clippedTime); if (calendar->DayOfWeek != trueDayOfWeek) { @@ -113,8 +113,8 @@ void DateCalculatorViewModel::OnInputsChanged() { if (m_IsDateDiffMode) { - DateTime clippedFromDate = ClipTime(FromDate, true); - DateTime clippedToDate = ClipTime(ToDate, true); + DateTime clippedFromDate = ClipTime(FromDate); + DateTime clippedToDate = ClipTime(ToDate); // Calculate difference between two dates auto dateDiff = m_dateCalcEngine->TryGetDateDifference(clippedFromDate, clippedToDate, m_daysOutputFormat); diff --git a/src/CalcViewModel/DateCalculatorViewModel.h b/src/CalcViewModel/DateCalculatorViewModel.h index d9e73f4b..48573784 100644 --- a/src/CalcViewModel/DateCalculatorViewModel.h +++ b/src/CalcViewModel/DateCalculatorViewModel.h @@ -110,7 +110,7 @@ namespace CalculatorApp Platform::String ^ GetDateDiffString() const; Platform::String ^ GetDateDiffStringInDays() const; Platform::String ^ GetLocalizedNumberString(int value) const; - static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime, bool adjustToLocalTime); + static Windows::Foundation::DateTime ClipTime(Windows::Foundation::DateTime dateTime, bool adjustUsingLocalTime = false); property bool IsOutOfBound { diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp new file mode 100644 index 00000000..50592aa4 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.cpp @@ -0,0 +1,461 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "EquationViewModel.h" +#include "CalcViewModel\Common\LocalizationSettings.h" +#include "CalcViewModel\GraphingCalculatorEnums.h" + +using namespace CalculatorApp::Common; +using namespace Graphing; +using namespace Platform; +using namespace Platform::Collections; +using namespace std; +using namespace Windows::ApplicationModel::Resources; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::Foundation::Collections; +using namespace GraphControl; + +namespace CalculatorApp::ViewModel +{ + GridDisplayItems::GridDisplayItems() + : m_Expression{ "" } + , m_Direction{ "" } + { + } + + KeyGraphFeaturesItem::KeyGraphFeaturesItem() + : m_Title{ "" } + , m_DisplayItems{ ref new Vector() } + , m_GridItems{ ref new Vector() } + , m_IsText{ false } + { + } + + EquationViewModel::EquationViewModel(Equation ^ equation, int functionLabelIndex, Windows::UI::Color color, int colorIndex) + : m_AnalysisErrorVisible{ false } + , m_FunctionLabelIndex{ functionLabelIndex } + , m_KeyGraphFeaturesItems{ ref new Vector() } + , m_resourceLoader{ ::ResourceLoader::GetForCurrentView() } + { + if (equation == nullptr) + { + throw ref new InvalidArgumentException(L"Equation cannot be null"); + } + + GraphEquation = equation; + LineColor = color; + LineColorIndex = colorIndex; + IsLineEnabled = true; + } + + void EquationViewModel::PopulateKeyGraphFeatures(KeyGraphFeaturesInfo ^ graphEquation) + { + if (graphEquation->AnalysisError != 0) + { + AnalysisErrorVisible = true; + if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisCouldNotBePerformed)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisCouldNotBePerformed"); + } + else if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::AnalysisNotSupported)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFAnalysisNotSupported"); + } + else if (graphEquation->AnalysisError == static_cast(AnalysisErrorType::VariableIsNotX)) + { + AnalysisErrorString = m_resourceLoader->GetString(L"KGFVariableIsNotX"); + } + return; + } + + KeyGraphFeaturesItems->Clear(); + + AddKeyGraphFeature(m_resourceLoader->GetString(L"Domain"), graphEquation->Domain, m_resourceLoader->GetString(L"KGFDomainNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Range"), graphEquation->Range, m_resourceLoader->GetString(L"KGFRangeNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"XIntercept"), graphEquation->XIntercept, m_resourceLoader->GetString(L"KGFXInterceptNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"YIntercept"), graphEquation->YIntercept, m_resourceLoader->GetString(L"KGFYInterceptNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Minima"), graphEquation->Minima, m_resourceLoader->GetString(L"KGFMinimaNone")); + AddKeyGraphFeature(m_resourceLoader->GetString(L"Maxima"), graphEquation->Maxima, m_resourceLoader->GetString(L"KGFMaximaNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"InflectionPoints"), graphEquation->InflectionPoints, m_resourceLoader->GetString(L"KGFInflectionPointsNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"VerticalAsymptotes"), graphEquation->VerticalAsymptotes, m_resourceLoader->GetString(L"KGFVerticalAsymptotesNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"HorizontalAsymptotes"), + graphEquation->HorizontalAsymptotes, + m_resourceLoader->GetString(L"KGFHorizontalAsymptotesNone")); + AddKeyGraphFeature( + m_resourceLoader->GetString(L"ObliqueAsymptotes"), graphEquation->ObliqueAsymptotes, m_resourceLoader->GetString(L"KGFObliqueAsymptotesNone")); + AddParityKeyGraphFeature(graphEquation); + AddPeriodicityKeyGraphFeature(graphEquation); + AddMonotoncityKeyGraphFeature(graphEquation); + AddTooComplexKeyGraphFeature(graphEquation); + + AnalysisErrorVisible = false; + } + + void EquationViewModel::AddKeyGraphFeature(String ^ title, String ^ expression, String ^ errorString) + { + KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem(); + item->Title = title; + if (expression != L"") + { + item->DisplayItems->Append(expression); + item->IsText = false; + } + else + { + item->DisplayItems->Append(errorString); + item->IsText = true; + } + KeyGraphFeaturesItems->Append(item); + } + + void EquationViewModel::AddKeyGraphFeature(String ^ title, IVector ^ expressionVector, String ^ errorString) + { + KeyGraphFeaturesItem ^ item = ref new KeyGraphFeaturesItem(); + item->Title = title; + if (expressionVector->Size != 0) + { + for (auto expression : expressionVector) + { + item->DisplayItems->Append(expression); + } + item->IsText = false; + } + else + { + item->DisplayItems->Append(errorString); + item->IsText = true; + } + KeyGraphFeaturesItems->Append(item); + } + + void EquationViewModel::AddParityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ parityItem = ref new KeyGraphFeaturesItem(); + parityItem->Title = m_resourceLoader->GetString(L"Parity"); + switch (graphEquation->Parity) + { + case 0: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown")); + break; + case 1: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityOdd")); + break; + case 2: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityEven")); + break; + case 3: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityNeither")); + break; + default: + parityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFParityUnknown")); + } + parityItem->IsText = true; + + KeyGraphFeaturesItems->Append(parityItem); + } + + void EquationViewModel::AddPeriodicityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ periodicityItem = ref new KeyGraphFeaturesItem(); + periodicityItem->Title = m_resourceLoader->GetString(L"Periodicity"); + switch (graphEquation->PeriodicityDirection) + { + case 0: + // Periodicity is not supported or is too complex to calculate. + // Return out of this function without adding periodicity to KeyGraphFeatureItems. + // SetTooComplexFeaturesErrorProperty will set the too complex error when periodicity is supported and unknown + return; + case 1: + if (graphEquation->PeriodicityExpression == L"") + { + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityUnknown")); + periodicityItem->IsText = true; + } + else + { + periodicityItem->DisplayItems->Append(graphEquation->PeriodicityExpression); + periodicityItem->IsText = false; + } + break; + case 2: + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityNotPeriodic")); + periodicityItem->IsText = false; + break; + default: + periodicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFPeriodicityError")); + periodicityItem->IsText = true; + } + + KeyGraphFeaturesItems->Append(periodicityItem); + } + + void EquationViewModel::AddMonotoncityKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + KeyGraphFeaturesItem ^ monotonicityItem = ref new KeyGraphFeaturesItem(); + monotonicityItem->Title = m_resourceLoader->GetString(L"Monotonicity"); + if (graphEquation->Monotonicity->Size != 0) + { + for (auto item : graphEquation->Monotonicity) + { + GridDisplayItems ^ gridItem = ref new GridDisplayItems(); + gridItem->Expression = item->Key; + + auto monotonicityType = item->Value->Data(); + switch (*monotonicityType) + { + case '0': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityUnknown"); + break; + case '1': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityIncreasing"); + break; + case '2': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityDecreasing"); + break; + case '3': + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityConstant"); + break; + default: + gridItem->Direction = m_resourceLoader->GetString(L"KGFMonotonicityError"); + break; + } + + monotonicityItem->GridItems->Append(gridItem); + } + monotonicityItem->IsText = false; + } + else + { + monotonicityItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFMonotonicityError")); + monotonicityItem->IsText = true; + } + + KeyGraphFeaturesItems->Append(monotonicityItem); + } + + void EquationViewModel::AddTooComplexKeyGraphFeature(KeyGraphFeaturesInfo ^ graphEquation) + { + if (graphEquation->TooComplexFeatures <= 0) + { + return; + } + + Platform::String ^ separator = ref new String(LocalizationSettings::GetInstance().GetListSeparator().c_str()); + + wstring error; + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Domain) == KeyGraphFeaturesFlag::Domain) + { + error.append((m_resourceLoader->GetString(L"Domain") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Range) == KeyGraphFeaturesFlag::Range) + { + error.append((m_resourceLoader->GetString(L"Range") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Zeros) == KeyGraphFeaturesFlag::Zeros) + { + error.append((m_resourceLoader->GetString(L"XIntercept") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::YIntercept) == KeyGraphFeaturesFlag::YIntercept) + { + error.append((m_resourceLoader->GetString(L"YIntercept") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Parity) == KeyGraphFeaturesFlag::Parity) + { + error.append((m_resourceLoader->GetString(L"Parity") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Periodicity) == KeyGraphFeaturesFlag::Periodicity) + { + error.append((m_resourceLoader->GetString(L"Periodicity") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Minima) == KeyGraphFeaturesFlag::Minima) + { + error.append((m_resourceLoader->GetString(L"Minima") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::Maxima) == KeyGraphFeaturesFlag::Maxima) + { + error.append((m_resourceLoader->GetString(L"Maxima") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::InflectionPoints) == KeyGraphFeaturesFlag::InflectionPoints) + { + error.append((m_resourceLoader->GetString(L"InflectionPoints") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::VerticalAsymptotes) == KeyGraphFeaturesFlag::VerticalAsymptotes) + { + error.append((m_resourceLoader->GetString(L"VerticalAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::HorizontalAsymptotes) == KeyGraphFeaturesFlag::HorizontalAsymptotes) + { + error.append((m_resourceLoader->GetString(L"HorizontalAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::ObliqueAsymptotes) == KeyGraphFeaturesFlag::ObliqueAsymptotes) + { + error.append((m_resourceLoader->GetString(L"ObliqueAsymptotes") + separator + L" ")->Data()); + } + if ((graphEquation->TooComplexFeatures & KeyGraphFeaturesFlag::MonotoneIntervals) == KeyGraphFeaturesFlag::MonotoneIntervals) + { + error.append((m_resourceLoader->GetString(L"Monotonicity") + separator + L" ")->Data()); + } + + KeyGraphFeaturesItem ^ tooComplexItem = ref new KeyGraphFeaturesItem(); + tooComplexItem->DisplayItems->Append(m_resourceLoader->GetString(L"KGFTooComplexFeaturesError")); + tooComplexItem->DisplayItems->Append(ref new String(error.substr(0, (error.length() - (separator->Length() + 1))).c_str())); + tooComplexItem->IsText = true; + + KeyGraphFeaturesItems->Append(tooComplexItem); + } + + String ^ EquationViewModel::EquationErrorText(ErrorType errorType, int errorCode) + { + auto resLoader = ResourceLoader::GetForCurrentView(); + if (errorType == ::ErrorType::Evaluation) + { + switch (static_cast(errorCode)) + { + case (EvaluationErrorCode::Overflow): + return resLoader->GetString(L"Overflow"); + break; + case (EvaluationErrorCode::RequireRadiansMode): + return resLoader->GetString(L"RequireRadiansMode"); + break; + case (EvaluationErrorCode::TooComplexToSolve): + return resLoader->GetString(L"TooComplexToSolve"); + break; + case (EvaluationErrorCode::RequireDegreesMode): + return resLoader->GetString(L"RequireDegreesMode"); + break; + case (EvaluationErrorCode::FactorialInvalidArgument): + case (EvaluationErrorCode::Factorial2InvalidArgument): + return resLoader->GetString(L"FactorialInvalidArgument"); + break; + case (EvaluationErrorCode::FactorialCannotPerformOnLargeNumber): + return resLoader->GetString(L"FactorialCannotPerformOnLargeNumber"); + break; + case (EvaluationErrorCode::ModuloCannotPerformOnFloat): + return resLoader->GetString(L"ModuloCannotPerformOnFloat"); + break; + case (EvaluationErrorCode::EquationTooComplexToSolve): + case (EvaluationErrorCode::EquationTooComplexToSolveSymbolic): + case (EvaluationErrorCode::EquationTooComplexToPlot): + case (EvaluationErrorCode::InequalityTooComplexToSolve): + case (EvaluationErrorCode::GE_TooComplexToSolve): + return resLoader->GetString(L"TooComplexToSolve"); + break; + case (EvaluationErrorCode::EquationHasNoSolution): + case (EvaluationErrorCode::InequalityHasNoSolution): + return resLoader->GetString(L"EquationHasNoSolution"); + break; + case (EvaluationErrorCode::DivideByZero): + return resLoader->GetString(L"DivideByZero"); + break; + case (EvaluationErrorCode::MutuallyExclusiveConditions): + return resLoader->GetString(L"MutuallyExclusiveConditions"); + break; + case (EvaluationErrorCode::OutOfDomain): + return resLoader->GetString(L"OutOfDomain"); + break; + case (EvaluationErrorCode::GE_NotSupported): + return resLoader->GetString(L"GE_NotSupported"); + break; + default: + return resLoader->GetString(L"GeneralError"); + break; + } + } + else if (errorType == ::ErrorType::Syntax) + { + switch (static_cast(errorCode)) + { + case (SyntaxErrorCode::ParenthesisMismatch): + return resLoader->GetString(L"ParenthesisMismatch"); + break; + case (SyntaxErrorCode::UnmatchedParenthesis): + return resLoader->GetString(L"UnmatchedParenthesis"); + break; + case (SyntaxErrorCode::TooManyDecimalPoints): + return resLoader->GetString(L"TooManyDecimalPoints"); + break; + case (SyntaxErrorCode::DecimalPointWithoutDigits): + return resLoader->GetString(L"DecimalPointWithoutDigits"); + break; + case (SyntaxErrorCode::UnexpectedEndOfExpression): + return resLoader->GetString(L"UnexpectedEndOfExpression"); + break; + case (SyntaxErrorCode::UnexpectedToken): + return resLoader->GetString(L"UnexpectedToken"); + break; + case (SyntaxErrorCode::InvalidToken): + return resLoader->GetString(L"InvalidToken"); + break; + case (SyntaxErrorCode::TooManyEquals): + return resLoader->GetString(L"TooManyEquals"); + break; + case (SyntaxErrorCode::EqualWithoutGraphVariable): + return resLoader->GetString(L"EqualWithoutGraphVariable"); + break; + case (SyntaxErrorCode::InvalidEquationSyntax): + case (SyntaxErrorCode::InvalidEquationFormat): + return resLoader->GetString(L"InvalidEquationSyntax"); + break; + case (SyntaxErrorCode::EmptyExpression): + return resLoader->GetString(L"EmptyExpression"); + break; + case (SyntaxErrorCode::EqualWithoutEquation): + return resLoader->GetString(L"EqualWithoutEquation"); + break; + case (SyntaxErrorCode::ExpectParenthesisAfterFunctionName): + return resLoader->GetString(L"ExpectParenthesisAfterFunctionName"); + break; + case (SyntaxErrorCode::IncorrectNumParameter): + return resLoader->GetString(L"IncorrectNumParameter"); + break; + case (SyntaxErrorCode::InvalidVariableNameFormat): + return resLoader->GetString(L"InvalidVariableNameFormat"); + break; + case (SyntaxErrorCode::BracketMismatch): + return resLoader->GetString(L"BracketMismatch"); + break; + case (SyntaxErrorCode::UnmatchedBracket): + return resLoader->GetString(L"UnmatchedBracket"); + break; + case (SyntaxErrorCode::CannotUseIInReal): + return resLoader->GetString(L"CannotUseIInReal"); + break; + case (SyntaxErrorCode::InvalidNumberDigit): + return resLoader->GetString(L"InvalidNumberDigit"); + break; + case (SyntaxErrorCode::InvalidNumberBase): + return resLoader->GetString(L"InvalidNumberBase"); + break; + case (SyntaxErrorCode::InvalidVariableSpecification): + return resLoader->GetString(L"InvalidVariableSpecification"); + break; + case (SyntaxErrorCode::ExpectingLogicalOperands): + case (SyntaxErrorCode::ExpectingScalarOperands): + return resLoader->GetString(L"ExpectingLogicalOperands"); + break; + case (SyntaxErrorCode::CannotUseIndexVarInOpLimits): + return resLoader->GetString(L"CannotUseIndexVarInOpLimits"); + break; + case (SyntaxErrorCode::CannotUseIndexVarInLimPoint): + return resLoader->GetString(L"Overflow"); + break; + case (SyntaxErrorCode::CannotUseComplexInfinityInReal): + return resLoader->GetString(L"CannotUseComplexInfinityInReal"); + break; + case (SyntaxErrorCode::CannotUseIInInequalitySolving): + return resLoader->GetString(L"CannotUseIInInequalitySolving"); + break; + default: + return resLoader->GetString(L"GeneralError"); + break; + } + } + + return resLoader->GetString(L"GeneralError"); + } +} diff --git a/src/CalcViewModel/GraphingCalculator/EquationViewModel.h b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h new file mode 100644 index 00000000..292e857d --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/EquationViewModel.h @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" + +namespace GraphControl +{ + ref class Equation; + ref class KeyGraphFeaturesInfo; +} + +namespace CalculatorApp::ViewModel +{ +public + ref class GridDisplayItems sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + GridDisplayItems(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Expression); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Direction); + }; + +public + ref class KeyGraphFeaturesItem sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + KeyGraphFeaturesItem(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_RW(Platform::String ^, Title); + OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, DisplayItems); + OBSERVABLE_PROPERTY_RW(Windows::Foundation::Collections::IObservableVector ^, GridItems); + OBSERVABLE_PROPERTY_RW(bool, IsText); + }; + +public + ref class EquationViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + EquationViewModel(GraphControl::Equation ^ equation, int functionLabelIndex, Windows::UI::Color color, int colorIndex); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(GraphControl::Equation ^, GraphEquation); + OBSERVABLE_PROPERTY_RW(int, FunctionLabelIndex); + OBSERVABLE_PROPERTY_RW(bool, IsLastItemInList); + PROPERTY_RW(int, LineColorIndex); + + property Platform::String ^ Expression + { + Platform::String ^ get() + { + return GraphEquation->Expression; + } + void set(Platform::String ^ value) + { + if (GraphEquation->Expression != value) + { + GraphEquation->Expression = value; + RaisePropertyChanged("Expression"); + } + } + } + + property Windows::UI::Color LineColor + { + Windows::UI::Color get() + { + return GraphEquation->LineColor; + } + void set(Windows::UI::Color value) + { + if (!Utils::AreColorsEqual(GraphEquation->LineColor, value)) + { + GraphEquation->LineColor = value; + RaisePropertyChanged("LineColor"); + } + } + } + + property bool IsLineEnabled + { + bool get() + { + return GraphEquation->IsLineEnabled; + } + void set(bool value) + { + if (GraphEquation->IsLineEnabled != value) + { + GraphEquation->IsLineEnabled = value; + RaisePropertyChanged("IsLineEnabled"); + } + } + } + + // Key Graph Features + OBSERVABLE_PROPERTY_R(Platform::String ^, AnalysisErrorString); + OBSERVABLE_PROPERTY_R(bool, AnalysisErrorVisible); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, KeyGraphFeaturesItems) + + void PopulateKeyGraphFeatures(GraphControl::KeyGraphFeaturesInfo ^ info); + + static Platform::String ^ EquationErrorText(GraphControl::ErrorType errorType, int errorCode); + + private: + void AddKeyGraphFeature(Platform::String ^ title, Platform::String ^ expression, Platform::String ^ errorString); + void AddKeyGraphFeature( + Platform::String ^ title, + Windows::Foundation::Collections::IVector ^ expressionVector, + Platform::String ^ errorString); + void AddParityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddPeriodicityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddMonotoncityKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + void AddTooComplexKeyGraphFeature(GraphControl::KeyGraphFeaturesInfo ^ info); + + Windows::Foundation::Collections::IObservableMap ^ m_Monotonicity; + Windows::ApplicationModel::Resources::ResourceLoader ^ m_resourceLoader; + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp new file mode 100644 index 00000000..4e605612 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.cpp @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "GraphingCalculatorViewModel.h" + +using namespace CalculatorApp::ViewModel; +using namespace Platform; +using namespace Platform::Collections; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml::Data; +using namespace GraphControl; + +namespace CalculatorApp::ViewModel +{ + GraphingCalculatorViewModel::GraphingCalculatorViewModel() + : m_IsDecimalEnabled{ true } + , m_Equations{ ref new Vector() } + , m_Variables{ ref new Vector() } + { + } + + void GraphingCalculatorViewModel::OnButtonPressed(Object ^ parameter) + { + } + + void GraphingCalculatorViewModel::UpdateVariables(IMap ^ variables) + { + Variables->Clear(); + for (auto variablePair : variables) + { + auto variable = ref new VariableViewModel(variablePair->Key, variablePair->Value); + variable->VariableUpdated += ref new EventHandler([this, variable](Object ^ sender, VariableChangedEventArgs e) { + VariableUpdated(variable, VariableChangedEventArgs{ e.variableName, e.newValue }); + }); + Variables->Append(variable); + } + } + + void GraphingCalculatorViewModel::SetSelectedEquation(EquationViewModel ^ equation) + { + SelectedEquation = equation; + } +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h new file mode 100644 index 00000000..eabd88e9 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingCalculatorViewModel.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" +#include "EquationViewModel.h" +#include "VariableViewModel.h" + +namespace CalculatorApp::ViewModel +{ + [Windows::UI::Xaml::Data::Bindable] public ref class GraphingCalculatorViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + GraphingCalculatorViewModel(); + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(bool, IsDecimalEnabled); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Equations); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Variables); + OBSERVABLE_PROPERTY_R(EquationViewModel ^, SelectedEquation); + + COMMAND_FOR_METHOD(ButtonPressed, GraphingCalculatorViewModel::OnButtonPressed); + + event Windows::Foundation::EventHandler ^ VariableUpdated; + + void UpdateVariables(Windows::Foundation::Collections::IMap ^ variables); + + void SetSelectedEquation(EquationViewModel ^ equation); + private: + void OnButtonPressed(Platform::Object ^ parameter); + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp new file mode 100644 index 00000000..3c7de353 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.cpp @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "GraphingSettingsViewModel.h" +#include + +using namespace CalculatorApp::ViewModel; +using namespace CalcManager::NumberFormattingUtils; +using namespace GraphControl; +using namespace std; +using namespace Platform; +using namespace Windows::UI::Xaml; + +GraphingSettingsViewModel::GraphingSettingsViewModel() + : m_XMinValue(0) + , m_XMaxValue(0) + , m_YMinValue(0) + , m_YMaxValue(0) + , m_XMinError(false) + , m_XMaxError(false) + , m_YMinError(false) + , m_YMaxError(false) + , m_dontUpdateDisplayRange() + , m_XIsMinLastChanged(true) + , m_YIsMinLastChanged(true) +{ +} + +void GraphingSettingsViewModel::SetGrapher(Grapher ^ grapher) +{ + if (grapher != nullptr) + { + if (grapher->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Invalid) + { + grapher->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians; + } + } + Graph = grapher; + + InitRanges(); + RaisePropertyChanged(L"TrigUnit"); +} + +void GraphingSettingsViewModel::InitRanges() +{ + double xMin = 0, xMax = 0, yMin = 0, yMax = 0; + if (m_Graph != nullptr) + { + m_Graph->GetDisplayRanges(&xMin, &xMax, &yMin, &yMax); + } + m_dontUpdateDisplayRange = true; + m_XMinValue = xMin; + m_XMaxValue = xMax; + m_YMinValue = yMin; + m_YMaxValue = yMax; + auto valueStr = to_wstring(m_XMinValue); + TrimTrailingZeros(valueStr); + XMin = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_XMaxValue); + TrimTrailingZeros(valueStr); + XMax = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_YMinValue); + TrimTrailingZeros(valueStr); + YMin = ref new String(valueStr.c_str()); + + valueStr = to_wstring(m_YMaxValue); + TrimTrailingZeros(valueStr); + YMax = ref new String(valueStr.c_str()); + + m_dontUpdateDisplayRange = false; +} + +void GraphingSettingsViewModel::ResetView() +{ + if (m_Graph != nullptr) + { + m_Graph->ResetGrid(); + InitRanges(); + m_XMinError = false; + m_XMaxError = false; + m_YMinError = false; + m_YMaxError = false; + + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMin"); + RaisePropertyChanged("XMax"); + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMin"); + RaisePropertyChanged("YMax"); + } +} + +void GraphingSettingsViewModel::UpdateDisplayRange() +{ + if (m_Graph == nullptr || m_dontUpdateDisplayRange || HasError()) + { + return; + } + + m_Graph->SetDisplayRanges(m_XMinValue, m_XMaxValue, m_YMinValue, m_YMaxValue); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::Grid, L""); +} + +bool GraphingSettingsViewModel::HasError() +{ + return m_XMinError || m_YMinError || m_XMaxError || m_YMaxError || XError || YError; +} diff --git a/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h new file mode 100644 index 00000000..6d6045c0 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/GraphingSettingsViewModel.h @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "../Common/Utils.h" +#include "CalcViewModel/Common/TraceLogger.h" + +namespace CalculatorApp::ViewModel +{ +#pragma once + [Windows::UI::Xaml::Data::Bindable] public ref class GraphingSettingsViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(bool, YMinError); + OBSERVABLE_PROPERTY_R(bool, XMinError); + OBSERVABLE_PROPERTY_R(bool, XMaxError); + OBSERVABLE_PROPERTY_R(bool, YMaxError); + OBSERVABLE_PROPERTY_R(GraphControl::Grapher ^, Graph); + + GraphingSettingsViewModel(); + + property bool XError + { + bool get() + { + return !m_XMinError && !m_XMaxError && m_XMinValue >= m_XMaxValue; + } + } + + property bool YError + { + bool get() + { + return !m_YMinError && !m_YMaxError && m_YMinValue >= m_YMaxValue; + } + } + + property Platform::String ^ XMin + { + Platform::String ^ get() + { + return m_XMin; + } + void set(Platform::String ^ value) + { + if (m_XMin == value) + { + return; + } + m_XMin = value; + m_XIsMinLastChanged = true; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->XAxisMin = m_XMinValue = number; + XMinError = false; + } + else + { + XMinError = true; + } + } + catch (...) + { + XMinError = true; + } + } + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMin"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ XMax + { + Platform::String ^ get() + { + return m_XMax; + } + void set(Platform::String ^ value) + { + if (m_XMax == value) + { + return; + } + m_XMax = value; + m_XIsMinLastChanged = false; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->XAxisMax = m_XMaxValue = number; + XMaxError = false; + } + else + { + XMaxError = true; + } + } + catch (...) + { + XMaxError = true; + } + } + RaisePropertyChanged("XError"); + RaisePropertyChanged("XMax"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ YMin + { + Platform::String ^ get() + { + return m_YMin; + } + void set(Platform::String ^ value) + { + if (m_YMin == value) + { + return; + } + m_YMin = value; + m_YIsMinLastChanged = true; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->YAxisMin = m_YMinValue = number; + YMinError = false; + } + else + { + YMinError = true; + } + } + catch (...) + { + YMinError = true; + } + } + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMin"); + UpdateDisplayRange(); + } + } + + property Platform::String ^ YMax + { + Platform::String ^ get() + { + return m_YMax; + } + void set(Platform::String ^ value) + { + if (m_YMax == value) + { + return; + } + m_YMax = value; + m_YIsMinLastChanged = false; + if (m_Graph != nullptr) + { + try + { + size_t sz; + auto number = std::stod(value->Data(), &sz); + if (value->Length() == sz) + { + m_Graph->YAxisMax = m_YMaxValue = number; + YMaxError = false; + } + else + { + YMaxError = true; + } + } + catch (...) + { + YMaxError = true; + } + } + RaisePropertyChanged("YError"); + RaisePropertyChanged("YMax"); + UpdateDisplayRange(); + } + } + + property int TrigUnit + { + int get() + { + return m_Graph == nullptr ? (int)Graphing::EvalTrigUnitMode::Invalid : m_Graph->TrigUnitMode; + } + void set(int value) + { + if (m_Graph == nullptr) + { + return; + } + m_Graph->TrigUnitMode = value; + RaisePropertyChanged(L"TrigUnit"); + } + } + + property bool TrigModeRadians + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Radians; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Radians) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Radians; + + RaisePropertyChanged(L"TrigModeRadians"); + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeGradians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Radians"); + } + } + } + + property bool TrigModeDegrees + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Degrees; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Degrees) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Degrees; + + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeRadians"); + RaisePropertyChanged(L"TrigModeGradians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Degrees"); + } + } + } + + property bool TrigModeGradians + { + bool get() + { + return m_Graph != nullptr && m_Graph->TrigUnitMode == (int)Graphing::EvalTrigUnitMode::Grads; + } + void set(bool value) + { + if (value && m_Graph != nullptr && m_Graph->TrigUnitMode != (int)Graphing::EvalTrigUnitMode::Grads) + { + m_Graph->TrigUnitMode = (int)Graphing::EvalTrigUnitMode::Grads; + + RaisePropertyChanged(L"TrigModeGradians"); + RaisePropertyChanged(L"TrigModeDegrees"); + RaisePropertyChanged(L"TrigModeRadians"); + + TraceLogger::GetInstance()->LogGraphSettingsChanged(GraphSettingsType::TrigUnits, L"Gradians"); + } + } + } + + public: + void UpdateDisplayRange(); + + public: + void SetGrapher(GraphControl::Grapher ^ grapher); + void InitRanges(); + void ResetView(); + bool HasError(); + + private: + Platform::String ^ m_XMin; + Platform::String ^ m_XMax; + Platform::String ^ m_YMin; + Platform::String ^ m_YMax; + double m_XMinValue; + double m_XMaxValue; + double m_YMinValue; + double m_YMaxValue; + bool m_dontUpdateDisplayRange; + bool m_XIsMinLastChanged; + bool m_YIsMinLastChanged; + }; +} diff --git a/src/CalcViewModel/GraphingCalculator/VariableViewModel.h b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h new file mode 100644 index 00000000..151499ec --- /dev/null +++ b/src/CalcViewModel/GraphingCalculator/VariableViewModel.h @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "../Common/Utils.h" +#include "CalcViewModel/Common/LocalizationStringUtil.h" +#include "EquationViewModel.h" + +namespace CalculatorApp::ViewModel +{ + +inline constexpr int DefaultMinMaxRange = 10; + +public + value struct VariableChangedEventArgs sealed + { + Platform::String ^ variableName; + double newValue; + }; + + [Windows::UI::Xaml::Data::Bindable] public ref class VariableViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged + { + public: + VariableViewModel(Platform::String ^ name, GraphControl::Variable ^ variable) + : m_Name(name) + , m_variable{ variable } + , m_SliderSettingsVisible(false) + { + } + + OBSERVABLE_OBJECT(); + OBSERVABLE_PROPERTY_R(Platform::String ^, Name); + OBSERVABLE_PROPERTY_RW(bool, SliderSettingsVisible); + + property double Min + { + double get() + { + return m_variable->Min; + } + void set(double value) + { + if (m_variable->Min != value) + { + if (value >= m_variable->Max) + { + m_variable->Max = value + DefaultMinMaxRange; + RaisePropertyChanged("Max"); + } + + m_variable->Min = value; + RaisePropertyChanged("Min"); + } + } + } + + property double Step + { + double get() + { + return m_variable->Step; + } + void set(double value) + { + if (m_variable->Step != value) + { + m_variable->Step = value; + RaisePropertyChanged("Step"); + } + } + } + + property double Max + { + double get() + { + return m_variable->Max; + } + void set(double value) + { + if (m_variable->Max != value) + { + if (value <= m_variable->Min) + { + m_variable->Min = value - DefaultMinMaxRange; + RaisePropertyChanged("Min"); + } + + m_variable->Max = value; + RaisePropertyChanged("Max"); + } + } + } + + event Windows::Foundation::EventHandler ^ VariableUpdated; + + property double Value + { + double get() + { + return m_variable->Value; + } + void set(double value) + { + if (value < m_variable->Min) + { + m_variable->Min = value; + RaisePropertyChanged(L"Min"); + } + else if (value > m_variable->Max) + { + m_variable->Max = value; + RaisePropertyChanged(L"Max"); + } + + if (m_variable->Value != value) + { + m_variable->Value = value; + VariableUpdated(this, VariableChangedEventArgs{ Name, value }); + RaisePropertyChanged(L"Value"); + } + } + } + + property Platform::String ^ VariableAutomationName + { + Platform::String ^ get() + { + return CalculatorApp::Common::LocalizationStringUtil::GetLocalizedString( + CalculatorApp::AppResourceProvider::GetInstance()->GetResourceString(L"VariableListViewItem"), Name); + } + } + + private: + GraphControl::Variable ^ m_variable; + }; +} diff --git a/src/CalcViewModel/GraphingCalculatorEnums.h b/src/CalcViewModel/GraphingCalculatorEnums.h new file mode 100644 index 00000000..8093f4a6 --- /dev/null +++ b/src/CalcViewModel/GraphingCalculatorEnums.h @@ -0,0 +1,29 @@ +#pragma once + +namespace CalculatorApp +{ + enum KeyGraphFeaturesFlag + { + Domain = 1, + Range = 2, + Parity = 4, + Periodicity = 8, + Zeros = 16, + YIntercept = 32, + Minima = 64, + Maxima = 128, + InflectionPoints = 256, + VerticalAsymptotes = 512, + HorizontalAsymptotes = 1024, + ObliqueAsymptotes = 2048, + MonotoneIntervals = 4096 + }; + + enum AnalysisErrorType + { + NoError, + AnalysisCouldNotBePerformed, + AnalysisNotSupported, + VariableIsNotX + }; +} diff --git a/src/CalcViewModel/HistoryItemViewModel.h b/src/CalcViewModel/HistoryItemViewModel.h index cc479518..0e30b0c8 100644 --- a/src/CalcViewModel/HistoryItemViewModel.h +++ b/src/CalcViewModel/HistoryItemViewModel.h @@ -9,7 +9,7 @@ namespace CalculatorApp { namespace ViewModel { - [Windows::UI::Xaml::Data::Bindable] public ref class HistoryItemViewModel sealed : Windows::UI::Xaml::Data::ICustomPropertyProvider + [Windows::UI::Xaml::Data::Bindable] public ref class HistoryItemViewModel sealed { internal : @@ -42,23 +42,6 @@ namespace CalculatorApp property Platform::String ^ AccResult { Platform::String ^ get() { return m_accResult; } } - virtual Windows::UI::Xaml::Data::ICustomProperty - ^ GetCustomProperty(Platform::String ^ name) { return nullptr; } - - virtual Windows::UI::Xaml::Data::ICustomProperty - ^ GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type) { return nullptr; } - - virtual property Windows::UI::Xaml::Interop::TypeName Type - { - Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - virtual Platform::String - ^ GetStringRepresentation() { return m_accExpression + " " + m_accResult; } - private : static Platform::String ^ GetAccessibleExpressionFromTokens( _In_ std::shared_ptr>> const& spTokens, diff --git a/src/CalcViewModel/HistoryViewModel.cpp b/src/CalcViewModel/HistoryViewModel.cpp index 58bd726d..8235c93a 100644 --- a/src/CalcViewModel/HistoryViewModel.cpp +++ b/src/CalcViewModel/HistoryViewModel.cpp @@ -6,6 +6,7 @@ #include "Common/TraceLogger.h" #include "Common/LocalizationStringUtil.h" #include "Common/LocalizationSettings.h" +#include "StandardCalculatorViewModel.h" using namespace CalculatorApp; using namespace CalculatorApp::Common; @@ -20,26 +21,22 @@ using namespace Windows::Security::Cryptography; using namespace Windows::Foundation::Collections; static StringReference HistoryVectorLengthKey{ L"HistoryVectorLength" }; +static StringReference ItemsSizeKey{ L"ItemsCount" }; -namespace CalculatorApp::ViewModel::HistoryResourceKeys +namespace HistoryResourceKeys { StringReference HistoryCleared(L"HistoryList_Cleared"); + StringReference HistorySlotCleared(L"Format_HistorySlotCleared"); } HistoryViewModel::HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager) : m_calculatorManager(calculatorManager) , m_localizedHistoryCleared(nullptr) + , m_localizedHistorySlotCleared(nullptr) { AreHistoryShortcutsEnabled = true; Items = ref new Platform::Collections::Vector(); - ItemSize = 0; -} - -void HistoryViewModel::RestoreCompleteHistory() -{ - RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_STD); - RestoreHistory(CalculationManager::CALCULATOR_MODE::CM_SCI); } // this will reload Items with the history list based on current mode @@ -47,11 +44,11 @@ void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode) { if (currentMode == ViewMode::Standard) { - m_currentMode = CalculationManager::CALCULATOR_MODE::CM_STD; + m_currentMode = CalculationManager::CalculatorMode::Standard; } else if (currentMode == ViewMode::Scientific) { - m_currentMode = CalculationManager::CALCULATOR_MODE::CM_SCI; + m_currentMode = CalculationManager::CalculatorMode::Scientific; } else { @@ -80,7 +77,7 @@ void HistoryViewModel::ReloadHistory(_In_ ViewMode currentMode) } Items = historyListVM; - UpdateItemSize(); + RaisePropertyChanged(ItemsSizeKey); } void HistoryViewModel::OnHistoryItemAdded(_In_ unsigned int addedItemIndex) @@ -106,8 +103,7 @@ void HistoryViewModel::OnHistoryItemAdded(_In_ unsigned int addedItemIndex) assert(addedItemIndex <= m_calculatorManager->MaxHistorySize() && addedItemIndex >= 0); Items->InsertAt(0, item); - UpdateItemSize(); - SaveHistory(); + RaisePropertyChanged(ItemsSizeKey); } void HistoryViewModel::SetCalculatorDisplay(CalculatorDisplay& calculatorDisplay) @@ -120,7 +116,7 @@ void HistoryViewModel::ShowItem(_In_ HistoryItemViewModel ^ e) { unsigned int index; Items->IndexOf(e, &index); - TraceLogger::GetInstance()->LogHistoryItemLoad((ViewMode)m_currentMode, ItemSize, (int)(index)); + TraceLogger::GetInstance()->LogHistoryItemLoad((ViewMode)m_currentMode, Items->Size, (int)(index)); HistoryItemClicked(e); } @@ -131,17 +127,16 @@ void HistoryViewModel::DeleteItem(_In_ HistoryItemViewModel ^ e) { if (m_calculatorManager->RemoveHistoryItem(itemIndex)) { - // Keys for the history container are index based. - // SaveHistory() re-inserts the items anyway, so it's faster to just clear out the container. - CalculationManager::CALCULATOR_MODE currentMode = m_currentMode; - ApplicationDataContainer ^ historyContainer = GetHistoryContainer(currentMode); - historyContainer->Values->Clear(); - Items->RemoveAt(itemIndex); - UpdateItemSize(); - SaveHistory(); + RaisePropertyChanged(ItemsSizeKey); } } + // Adding 1 to the history item index to provide 1-based numbering on announcements. + wstring localizedIndex = to_wstring(itemIndex + 1); + LocalizationSettings::GetInstance().LocalizeDisplayValue(&localizedIndex); + m_localizedHistorySlotCleared = AppResourceProvider::GetInstance()->GetResourceString(HistoryResourceKeys::HistorySlotCleared); + String ^ announcement = LocalizationStringUtil::GetLocalizedString(m_localizedHistorySlotCleared, StringReference(localizedIndex.c_str())); + HistoryAnnouncement = CalculatorAnnouncement::GetHistorySlotClearedAnnouncement(announcement); } void HistoryViewModel::OnHideCommand(_In_ Platform::Object ^ e) @@ -152,16 +147,14 @@ void HistoryViewModel::OnHideCommand(_In_ Platform::Object ^ e) void HistoryViewModel::OnClearCommand(_In_ Platform::Object ^ e) { - if (AreHistoryShortcutsEnabled == true) + if (AreHistoryShortcutsEnabled) { m_calculatorManager->ClearHistory(); if (Items->Size > 0) { - CalculationManager::CALCULATOR_MODE currentMode = m_currentMode; - ClearHistoryContainer(currentMode); Items->Clear(); - UpdateItemSize(); + RaisePropertyChanged(ItemsSizeKey); } if (m_localizedHistoryCleared == nullptr) @@ -172,205 +165,7 @@ void HistoryViewModel::OnClearCommand(_In_ Platform::Object ^ e) } } -// this method restores history vector per mode -void HistoryViewModel::RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode) +unsigned long long HistoryViewModel::GetMaxItemSize() { - ApplicationDataContainer ^ historyContainer = GetHistoryContainer(cMode); - std::shared_ptr>> historyVector = - std::make_shared>>(); - auto historyVectorLength = static_cast(historyContainer->Values->Lookup(HistoryVectorLengthKey)); - bool failure = false; - - if (historyVectorLength > 0) - { - for (int i = 0; i < historyVectorLength; ++i) - { - try - { - // deserialize each item - auto item = DeserializeHistoryItem(i.ToString(), historyContainer); - std::shared_ptr Item = std::make_shared(item); - historyVector->push_back(Item); - } - catch (Platform::Exception ^ e) - { - failure = true; - break; - } - } - - if (!failure) - { - // if task has been cancelled set history to 0 - m_calculatorManager->SetHistory(cMode, *historyVector); - - // update length once again for consistency between stored number of items and length - UpdateHistoryVectorLength(static_cast(historyVector->size()), cMode); - } - else - { - // in case of failure do not show any item - UpdateHistoryVectorLength(0, cMode); - } - } -} - -Platform::String ^ HistoryViewModel::GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - Platform::ValueType ^ modeValue = static_cast(cMode); - return Platform::String::Concat(modeValue->ToString(), L"_History"); -} - -ApplicationDataContainer ^ HistoryViewModel::GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; - ApplicationDataContainer ^ historyContainer; - - // naming container based on mode - Platform::String ^ historyContainerKey = GetHistoryContainerKey(cMode); - - if (localSettings->Containers->HasKey(historyContainerKey)) - { - historyContainer = localSettings->Containers->Lookup(historyContainerKey); - } - else - { - // create container for adding data - historyContainer = localSettings->CreateContainer(historyContainerKey, ApplicationDataCreateDisposition::Always); - int initialHistoryVectorLength = 0; - historyContainer->Values->Insert(HistoryVectorLengthKey, initialHistoryVectorLength); - } - - return historyContainer; -} - -void HistoryViewModel::ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer ^ localSettings = ApplicationData::Current->LocalSettings; - localSettings->DeleteContainer(GetHistoryContainerKey(cMode)); -} - -// this method will be used to update the history item length -void HistoryViewModel::UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode) -{ - ApplicationDataContainer ^ historyContainer = GetHistoryContainer(cMode); - historyContainer->Values->Remove(HistoryVectorLengthKey); - historyContainer->Values->Insert(HistoryVectorLengthKey, newValue); -} - -void HistoryViewModel::ClearHistory() -{ - ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_STD); - ClearHistoryContainer(CalculationManager::CALCULATOR_MODE::CM_SCI); -} - -void HistoryViewModel::SaveHistory() -{ - ApplicationDataContainer ^ historyContainer = GetHistoryContainer(m_currentMode); - auto const& currentHistoryVector = m_calculatorManager->GetHistoryItems(m_currentMode); - bool failure = false; - int index = 0; - Platform::String ^ serializedHistoryItem; - - for (auto const& item : currentHistoryVector) - { - try - { - serializedHistoryItem = SerializeHistoryItem(item); - historyContainer->Values->Insert(index.ToString(), serializedHistoryItem); - } - catch (Platform::Exception ^) - { - failure = true; - break; - } - - ++index; - } - - if (!failure) - { - // insertion is successful - UpdateHistoryVectorLength(static_cast(currentHistoryVector.size()), m_currentMode); - } - else - { - UpdateHistoryVectorLength(0, m_currentMode); - } -} - -// this serializes a history item into a base64 encoded string -Platform::String ^ HistoryViewModel::SerializeHistoryItem(_In_ std::shared_ptr const& item) -{ - DataWriter ^ writer = ref new DataWriter(); - auto expr = item->historyItemVector.expression; - auto result = item->historyItemVector.result; - auto platformExpr = ref new Platform::String(expr.c_str()); - writer->WriteUInt32(writer->MeasureString(platformExpr)); - writer->WriteString(platformExpr); - auto platformResult = ref new Platform::String(result.c_str()); - writer->WriteUInt32(writer->MeasureString(platformResult)); - writer->WriteString(platformResult); - - Utils::SerializeCommandsAndTokens(item->historyItemVector.spTokens, item->historyItemVector.spCommands, writer); - - IBuffer ^ buffer = writer->DetachBuffer(); - if (buffer == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - return CryptographicBuffer::EncodeToBase64String(buffer); -} - -CalculationManager::HISTORYITEM -HistoryViewModel::DeserializeHistoryItem(_In_ Platform::String ^ historyItemKey, _In_ ApplicationDataContainer ^ historyContainer) -{ - CalculationManager::HISTORYITEM historyItem; - if (historyContainer->Values->HasKey(historyItemKey)) - { - Object ^ historyItemValues = historyContainer->Values->Lookup(historyItemKey); - - if (historyItemValues == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - String ^ historyData = safe_cast(historyItemValues); - IBuffer ^ buffer = CryptographicBuffer::DecodeFromBase64String(historyData); - - if (buffer == nullptr) - { - throw ref new Platform::Exception(E_POINTER, ref new Platform::String(L"History Item is NULL")); - } - - DataReader ^ reader = DataReader::FromBuffer(buffer); - auto exprLen = reader->ReadUInt32(); - auto expression = reader->ReadString(exprLen); - historyItem.historyItemVector.expression = expression->Data(); - - auto resultLen = reader->ReadUInt32(); - auto result = reader->ReadString(resultLen); - historyItem.historyItemVector.result = result->Data(); - - historyItem.historyItemVector.spCommands = Utils::DeserializeCommands(reader); - historyItem.historyItemVector.spTokens = Utils::DeserializeTokens(reader); - } - else - { - throw ref new Platform::Exception(E_ACCESSDENIED, ref new Platform::String(L"History Item not found")); - } - return historyItem; -} - -bool HistoryViewModel::IsValid(_In_ CalculationManager::HISTORYITEM item) -{ - return ( - !item.historyItemVector.expression.empty() && !item.historyItemVector.result.empty() && (bool)item.historyItemVector.spCommands - && (bool)item.historyItemVector.spTokens); -} - -void HistoryViewModel::UpdateItemSize() -{ - ItemSize = Items->Size; + return static_cast(m_calculatorManager->MaxHistorySize()); } diff --git a/src/CalcViewModel/HistoryViewModel.h b/src/CalcViewModel/HistoryViewModel.h index 75321d39..d7c7874e 100644 --- a/src/CalcViewModel/HistoryViewModel.h +++ b/src/CalcViewModel/HistoryViewModel.h @@ -15,6 +15,8 @@ namespace CalculatorApp namespace ViewModel { + ref class StandardCalculatorViewModel; + public delegate void HideHistoryClickedHandler(); public @@ -24,10 +26,16 @@ namespace CalculatorApp { public: OBSERVABLE_OBJECT(); - OBSERVABLE_PROPERTY_RW(int, ItemSize); - OBSERVABLE_PROPERTY_RW(Windows::UI::Xaml::Interop::IBindableObservableVector ^, Items); + OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Items); OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); - OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::Automation::NarratorAnnouncement ^, HistoryAnnouncement); + OBSERVABLE_PROPERTY_R(CalculatorApp::Common::Automation::NarratorAnnouncement ^, HistoryAnnouncement); + property int ItemsCount + { + int get() + { + return Items->Size; + } + } void OnHistoryItemAdded(_In_ unsigned int addedItemIndex); @@ -40,36 +48,20 @@ namespace CalculatorApp event HideHistoryClickedHandler ^ HideHistoryClicked; event HistoryItemClickedHandler ^ HistoryItemClicked; void ShowItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel ^ e); - void ClearHistory(); - void RestoreCompleteHistory(); internal : HistoryViewModel(_In_ CalculationManager::CalculatorManager* calculatorManager); void SetCalculatorDisplay(CalculatorDisplay& calculatorDisplay); void ReloadHistory(_In_ CalculatorApp::Common::ViewMode currentMode); + unsigned long long GetMaxItemSize(); void DeleteItem(_In_ CalculatorApp::ViewModel::HistoryItemViewModel ^ e); - // store history in app data functions - Platform::String ^ SerializeHistoryItem(_In_ std::shared_ptr const& item); - void SaveHistory(); - private: CalculationManager::CalculatorManager* const m_calculatorManager; CalculatorDisplay m_calculatorDisplay; - CalculationManager::CALCULATOR_MODE m_currentMode; + CalculationManager::CalculatorMode m_currentMode; Platform::String ^ m_localizedHistoryCleared; - - void RestoreHistory(_In_ CalculationManager::CALCULATOR_MODE cMode); - CalculationManager::HISTORYITEM - DeserializeHistoryItem(_In_ Platform::String ^ historyItemKey, _In_ Windows::Storage::ApplicationDataContainer ^ historyContainer); - Windows::Storage::ApplicationDataContainer ^ GetHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode); - Platform::String ^ GetHistoryContainerKey(_In_ CalculationManager::CALCULATOR_MODE cMode); - void ClearHistoryContainer(_In_ CalculationManager::CALCULATOR_MODE cMode); - void UpdateHistoryVectorLength(_In_ int newValue, _In_ CalculationManager::CALCULATOR_MODE cMode); - bool IsValid(_In_ CalculationManager::HISTORYITEM item); - - friend class CalculatorDisplay; - void UpdateItemSize(); + Platform::String ^ m_localizedHistorySlotCleared; }; } } diff --git a/src/CalcViewModel/MemoryItemViewModel.h b/src/CalcViewModel/MemoryItemViewModel.h index 9a85a2bc..fc5dce0c 100644 --- a/src/CalcViewModel/MemoryItemViewModel.h +++ b/src/CalcViewModel/MemoryItemViewModel.h @@ -14,8 +14,7 @@ namespace CalculatorApp /// /// Model representation of a single item in the Memory list /// - [Windows::UI::Xaml::Data::Bindable] public ref class MemoryItemViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged, - Windows::UI::Xaml::Data::ICustomPropertyProvider + [Windows::UI::Xaml::Data::Bindable] public ref class MemoryItemViewModel sealed : public Windows::UI::Xaml::Data::INotifyPropertyChanged { public: MemoryItemViewModel(StandardCalculatorViewModel ^ calcVM) @@ -27,24 +26,7 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_RW(int, Position); OBSERVABLE_PROPERTY_RW(Platform::String ^, Value); - virtual Windows::UI::Xaml::Data::ICustomProperty - ^ GetCustomProperty(Platform::String ^ name) { return nullptr; } - - virtual Windows::UI::Xaml::Data::ICustomProperty - ^ GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type) { return nullptr; } - - virtual property Windows::UI::Xaml::Interop::TypeName Type - { - Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - virtual Platform::String - ^ GetStringRepresentation() { return Value; } - - void Clear(); + void Clear(); void MemoryAdd(); void MemorySubtract(); diff --git a/src/CalcViewModel/StandardCalculatorViewModel.cpp b/src/CalcViewModel/StandardCalculatorViewModel.cpp index 3b3647dd..56f2bda4 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.cpp +++ b/src/CalcViewModel/StandardCalculatorViewModel.cpp @@ -41,6 +41,8 @@ namespace StringReference DisplayValuePropertyName(L"DisplayValue"); StringReference CalculationResultAutomationNamePropertyName(L"CalculationResultAutomationName"); StringReference IsBitFlipCheckedPropertyName(L"IsBitFlipChecked"); + StringReference CalcAlwaysOnTop(L"CalcAlwaysOnTop"); + StringReference CalcBackToFullView(L"CalcBackToFullView"); } namespace CalculatorResourceKeys @@ -130,27 +132,17 @@ StandardCalculatorViewModel::StandardCalculatorViewModel() IsOperandEnabled = true; IsNegateEnabled = true; IsDecimalEnabled = true; - AreHistoryShortcutsEnabled = true; - AreProgrammerRadixOperatorsEnabled = false; + AreProgrammerRadixOperatorsVisible = false; } -String ^ StandardCalculatorViewModel::LocalizeDisplayValue(_In_ wstring const& displayValue, _In_ bool isError) +String ^ StandardCalculatorViewModel::LocalizeDisplayValue(_In_ wstring const& displayValue) { wstring result(displayValue); - LocalizationSettings::GetInstance().LocalizeDisplayValue(&result); - - // WINBLUE: 440747 - In BiDi languages, error messages need to be wrapped in LRE/PDF - if (isError && m_isRtlLanguage) - { - result.insert(result.begin(), Utils::LRE); - result.push_back(Utils::PDF); - } - return ref new Platform::String(result.c_str()); } -String ^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring const& displayValue, _In_ String ^ localizedDisplayValue, _In_ bool isError) +String ^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring const& displayValue, _In_ String ^ localizedDisplayValue) { String ^ localizedValue = localizedDisplayValue; String ^ automationFormat = m_localizedCalculationResultAutomationFormat; @@ -159,7 +151,7 @@ String ^ StandardCalculatorViewModel::CalculateNarratorDisplayValue(_In_ wstring if (Utils::IsLastCharacterTarget(displayValue, m_decimalSeparator)) { // remove the decimal separator, to avoid a long pause between words - localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.length() - 1), isError); + localizedValue = LocalizeDisplayValue(displayValue.substr(0, displayValue.length() - 1)); // Use a format which has a word in the decimal separator's place // "The Display is 10 point" @@ -195,11 +187,11 @@ String ^ StandardCalculatorViewModel::GetNarratorStringReadRawNumbers(_In_ Strin void StandardCalculatorViewModel::SetPrimaryDisplay(_In_ String ^ displayStringValue, _In_ bool isError) { - String ^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue->Data(), isError); + String ^ localizedDisplayStringValue = LocalizeDisplayValue(displayStringValue->Data()); // Set this variable before the DisplayValue is modified, Otherwise the DisplayValue will // not match what the narrator is saying - m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue->Data(), localizedDisplayStringValue, isError); + m_CalculationResultAutomationName = CalculateNarratorDisplayValue(displayStringValue->Data(), localizedDisplayStringValue); AreAlwaysOnTopResultsUpdated = false; if (DisplayValue != localizedDisplayStringValue) @@ -260,8 +252,7 @@ void StandardCalculatorViewModel::SetNoParenAddedNarratorAnnouncement() { if (m_localizedNoRightParenthesisAddedFormat == nullptr) { - m_localizedNoRightParenthesisAddedFormat = - AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::NoParenthesisAdded); + m_localizedNoRightParenthesisAddedFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::NoParenthesisAdded); } Announcement = CalculatorAnnouncement::GetNoRightParenthesisAddedAnnouncement(m_localizedNoRightParenthesisAddedFormat); @@ -418,7 +409,7 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector& new MemoryItemViewModel ^ memorySlot = ref new MemoryItemViewModel(this); memorySlot->Position = 0; localizer.LocalizeDisplayValue(&stringValue); - memorySlot->Value = Utils::LRO + ref new String(stringValue.c_str()) + Utils::PDF; + memorySlot->Value = ref new String(stringValue.c_str()); MemorizedNumbers->InsertAt(0, memorySlot); IsMemoryEmpty = IsAlwaysOnTop; @@ -440,7 +431,7 @@ void StandardCalculatorViewModel::SetMemorizedNumbers(const vector& new // If the value is different, update the value if (MemorizedNumbers->GetAt(i)->Value != StringReference(newStringValue.c_str())) { - MemorizedNumbers->GetAt(i)->Value = Utils::LRO + ref new String(newStringValue.c_str()) + Utils::PDF; + MemorizedNumbers->GetAt(i)->Value = ref new String(newStringValue.c_str()); } } } @@ -679,18 +670,18 @@ void StandardCalculatorViewModel::OnButtonPressed(Object ^ parameter) } } -RADIX_TYPE StandardCalculatorViewModel::GetRadixTypeFromNumberBase(NumberBase base) +RadixType StandardCalculatorViewModel::GetRadixTypeFromNumberBase(NumberBase base) { switch (base) { case NumberBase::BinBase: - return RADIX_TYPE::BIN_RADIX; + return RadixType::Binary; case NumberBase::HexBase: - return RADIX_TYPE::HEX_RADIX; + return RadixType::Hex; case NumberBase::OctBase: - return RADIX_TYPE::OCT_RADIX; + return RadixType::Octal; default: - return RADIX_TYPE::DEC_RADIX; + return RadixType::Decimal; } } @@ -903,21 +894,6 @@ void StandardCalculatorViewModel::OnClearMemoryCommand(Object ^ parameter) Announcement = CalculatorAnnouncement::GetMemoryClearedAnnouncement(m_localizedMemoryCleared); } -void StandardCalculatorViewModel::OnPinUnpinCommand(Object ^ parameter) -{ - SetViewPinnedState(!IsViewPinned()); -} - -bool StandardCalculatorViewModel::IsViewPinned() -{ - return m_IsCurrentViewPinned; -} - -void StandardCalculatorViewModel::SetViewPinnedState(bool pinned) -{ - IsCurrentViewPinned = pinned; -} - ButtonInfo StandardCalculatorViewModel::MapCharacterToButtonId(char16 ch) { ButtonInfo result; @@ -1027,8 +1003,8 @@ ButtonInfo StandardCalculatorViewModel::MapCharacterToButtonId(char16 ch) { if (LocalizationSettings::GetInstance().IsLocalizedDigit(ch)) { - result.buttonId = NumbersAndOperatorsEnum::Zero - + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0')); + result.buttonId = + NumbersAndOperatorsEnum::Zero + static_cast(ch - LocalizationSettings::GetInstance().GetDigitSymbolFromEnUsDigit('0')); result.canSendNegate = true; } } @@ -1187,6 +1163,19 @@ void StandardCalculatorViewModel::OnPropertyChanged(String ^ propertyname) TraceLogger::GetInstance()->UpdateButtonUsage( IsBitFlipChecked ? NumbersAndOperatorsEnum::BitflipButton : NumbersAndOperatorsEnum::FullKeypadButton, ViewMode::Programmer); } + else if (propertyname == IsAlwaysOnTopPropertyName) + { + String ^ announcement; + if (IsAlwaysOnTop) + { + announcement = AppResourceProvider::GetInstance()->GetResourceString(CalcAlwaysOnTop); + } + else + { + announcement = AppResourceProvider::GetInstance()->GetResourceString(CalcBackToFullView); + } + Announcement = CalculatorAnnouncement::GetAlwaysOnTopChangedAnnouncement(announcement); + } } void StandardCalculatorViewModel::SetCalculatorType(ViewMode targetState) @@ -1243,7 +1232,7 @@ void StandardCalculatorViewModel::ResetDisplay() { AreHEXButtonsEnabled = false; CurrentRadixType = NumberBase::DecBase; - m_standardCalculatorManager.SetRadix(DEC_RADIX); + m_standardCalculatorManager.SetRadix(RadixType::Decimal); } void StandardCalculatorViewModel::SetPrecision(int32_t precision) @@ -1268,16 +1257,16 @@ void StandardCalculatorViewModel::SetMemorizedNumbersString() m_standardCalculatorManager.SetMemorizedNumbersString(); } -ANGLE_TYPE GetAngleTypeFromCommand(Command command) +AngleType GetAngleTypeFromCommand(Command command) { switch (command) { case Command::CommandDEG: - return ANGLE_DEG; + return AngleType::Degrees; case Command::CommandRAD: - return ANGLE_RAD; + return AngleType::Radians; case Command::CommandGRAD: - return ANGLE_GRAD; + return AngleType::Gradians; default: throw ref new Exception(E_FAIL, L"Invalid command type"); } @@ -1294,7 +1283,7 @@ void StandardCalculatorViewModel::SaveEditedCommand(_In_ unsigned int tokenPosit if (IsUnaryOp(command) && command != Command::CommandSIGN) { int angleCmd = static_cast(m_standardCalculatorManager.GetCurrentDegreeMode()); - ANGLE_TYPE angleType = GetAngleTypeFromCommand(static_cast(angleCmd)); + AngleType angleType = GetAngleTypeFromCommand(static_cast(angleCmd)); if (IsTrigOp(command)) { @@ -1476,19 +1465,10 @@ void StandardCalculatorViewModel::Recalculate(bool fromHistory) } } -CommandType StandardCalculatorViewModel::GetSelectedTokenType(_In_ unsigned int tokenPosition) -{ - const pair& token = m_tokens->at(tokenPosition); - unsigned int tokenCommandIndex = token.second; - const shared_ptr& tokenCommand = m_commands->at(tokenCommandIndex); - - return tokenCommand->GetCommandType(); -} - bool StandardCalculatorViewModel::IsOpnd(Command command) { static constexpr Command opnd[] = { Command::Command0, Command::Command1, Command::Command2, Command::Command3, Command::Command4, Command::Command5, - Command::Command6, Command::Command7, Command::Command8, Command::Command9, Command::CommandPNT }; + Command::Command6, Command::Command7, Command::Command8, Command::Command9, Command::CommandPNT }; return find(begin(opnd), end(opnd), command) != end(opnd); } @@ -1496,9 +1476,9 @@ bool StandardCalculatorViewModel::IsOpnd(Command command) bool StandardCalculatorViewModel::IsUnaryOp(Command command) { static constexpr Command unaryOp[] = { Command::CommandSQRT, Command::CommandFAC, Command::CommandSQR, Command::CommandLOG, - Command::CommandPOW10, Command::CommandPOWE, Command::CommandLN, Command::CommandREC, - Command::CommandSIGN, Command::CommandSINH, Command::CommandASINH, Command::CommandCOSH, - Command::CommandACOSH, Command::CommandTANH, Command::CommandATANH, Command::CommandCUB }; + Command::CommandPOW10, Command::CommandPOWE, Command::CommandLN, Command::CommandREC, + Command::CommandSIGN, Command::CommandSINH, Command::CommandASINH, Command::CommandCOSH, + Command::CommandACOSH, Command::CommandTANH, Command::CommandATANH, Command::CommandCUB }; if (find(begin(unaryOp), end(unaryOp), command) != end(unaryOp)) { @@ -1515,9 +1495,8 @@ bool StandardCalculatorViewModel::IsUnaryOp(Command command) bool StandardCalculatorViewModel::IsTrigOp(Command command) { - static constexpr Command trigOp[] = { - Command::CommandSIN, Command::CommandCOS, Command::CommandTAN, Command::CommandASIN, Command::CommandACOS, Command::CommandATAN - }; + static constexpr Command trigOp[] = { Command::CommandSIN, Command::CommandCOS, Command::CommandTAN, + Command::CommandASIN, Command::CommandACOS, Command::CommandATAN }; return find(begin(trigOp), end(trigOp), command) != end(trigOp); } @@ -1525,7 +1504,7 @@ bool StandardCalculatorViewModel::IsTrigOp(Command command) bool StandardCalculatorViewModel::IsBinOp(Command command) { static constexpr Command binOp[] = { Command::CommandADD, Command::CommandSUB, Command::CommandMUL, Command::CommandDIV, - Command::CommandEXP, Command::CommandROOT, Command::CommandMOD, Command::CommandPWR }; + Command::CommandEXP, Command::CommandROOT, Command::CommandMOD, Command::CommandPWR }; return find(begin(binOp), end(binOp), command) != end(binOp); } @@ -1600,10 +1579,10 @@ void StandardCalculatorViewModel::UpdateProgrammerPanelDisplay() localizer.LocalizeDisplayValue(&octalDisplayString); localizer.LocalizeDisplayValue(&binaryDisplayString); - HexDisplayValue = Utils::LRO + ref new Platform::String(hexDisplayString.c_str()) + Utils::PDF; - DecimalDisplayValue = Utils::LRO + ref new Platform::String(decimalDisplayString.c_str()) + Utils::PDF; - OctalDisplayValue = Utils::LRO + ref new Platform::String(octalDisplayString.c_str()) + Utils::PDF; - BinaryDisplayValue = Utils::LRO + ref new Platform::String(binaryDisplayString.c_str()) + Utils::PDF; + HexDisplayValue = ref new Platform::String(hexDisplayString.c_str()); + DecimalDisplayValue = ref new Platform::String(decimalDisplayString.c_str()); + OctalDisplayValue = ref new Platform::String(octalDisplayString.c_str()); + BinaryDisplayValue = ref new Platform::String(binaryDisplayString.c_str()); HexDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedHexaDecimalAutomationFormat, GetNarratorStringReadRawNumbers(HexDisplayValue)); DecDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedDecimalAutomationFormat, DecimalDisplayValue); OctDisplayValue_AutomationName = GetLocalizedStringFormat(m_localizedOctalAutomationFormat, GetNarratorStringReadRawNumbers(OctalDisplayValue)); @@ -1626,26 +1605,6 @@ void StandardCalculatorViewModel::SwitchAngleType(NumbersAndOperatorsEnum num) OnButtonPressed(num); } -NumbersAndOperatorsEnum StandardCalculatorViewModel::ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter) -{ - NumbersAndOperatorsEnum angletype; - switch (parameter) - { - case 321: - angletype = NumbersAndOperatorsEnum::Degree; - break; - case 322: - angletype = NumbersAndOperatorsEnum::Radians; - break; - case 323: - angletype = NumbersAndOperatorsEnum::Grads; - break; - default: - angletype = NumbersAndOperatorsEnum::Degree; - }; - return angletype; -} - void StandardCalculatorViewModel::UpdateOperand(int pos, String ^ text) { pair p = m_tokens->at(pos); @@ -1708,66 +1667,6 @@ void StandardCalculatorViewModel::UpdateOperand(int pos, String ^ text) } } -void StandardCalculatorViewModel::UpdateCommandsInRecordingMode() -{ - shared_ptr> commands = make_shared>(); - bool isDecimal = false; - bool isNegative = false; - bool isExpMode = false; - bool ePlusMode = false; - bool eMinusMode = false; - - for (const auto savedCommand : m_standardCalculatorManager.GetSavedCommands()) - { - const Command val = static_cast(savedCommand); - if (val == Command::CommandSIGN) - { - isNegative = true; - continue; - } - else if ((val >= Command::Command0 && val <= Command::Command9)) - { - } - else if (val == Command::CommandPNT) - { - isDecimal = true; - } - else if (val == Command::CommandEXP) - { - isExpMode = true; - } - else if (isExpMode && !ePlusMode && (val == Command::CommandMPLUS)) - { - ePlusMode = true; - continue; - } - else if (isExpMode && !eMinusMode && (val == Command::CommandMMINUS)) - { - eMinusMode = true; - continue; - } - else - { - // Reset all vars - isDecimal = false; - isNegative = false; - isExpMode = false; - ePlusMode = false; - eMinusMode = false; - commands->clear(); - continue; - } - commands->push_back(static_cast(val)); - } - - if (!commands->empty()) - { - shared_ptr sp = make_shared(commands, isNegative, isDecimal, isExpMode); - m_commands->push_back(sp); - } - Recalculate(); -} - void StandardCalculatorViewModel::OnMaxDigitsReached() { if (m_localizedMaxDigitsReachedAutomationFormat == nullptr) @@ -1794,12 +1693,11 @@ NarratorAnnouncement ^ StandardCalculatorViewModel::GetDisplayUpdatedNarratorAnn { if (m_localizedButtonPressFeedbackAutomationFormat == nullptr) { - m_localizedButtonPressFeedbackAutomationFormat = AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::ButtonPressFeedbackFormat); + m_localizedButtonPressFeedbackAutomationFormat = + AppResourceProvider::GetInstance()->GetResourceString(CalculatorResourceKeys::ButtonPressFeedbackFormat); } announcement = LocalizationStringUtil::GetLocalizedString( - m_localizedButtonPressFeedbackAutomationFormat, - m_CalculationResultAutomationName, - m_feedbackForButtonPress); + m_localizedButtonPressFeedbackAutomationFormat, m_CalculationResultAutomationName, m_feedbackForButtonPress); } // Make sure we don't accidentally repeat an announcement. @@ -1856,3 +1754,18 @@ void StandardCalculatorViewModel::SelectHistoryItem(HistoryItemViewModel ^ item) SetPrimaryDisplay(item->Result, false); IsFToEEnabled = false; } + +void StandardCalculatorViewModel::ResetCalcManager(bool clearMemory) +{ + m_standardCalculatorManager.Reset(clearMemory); +} + +void StandardCalculatorViewModel::SendCommandToCalcManager(int commandId) +{ + m_standardCalculatorManager.SendCommand(static_cast(commandId)); +} + +void StandardCalculatorViewModel::SetBitshiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement) +{ + Announcement = CalculatorAnnouncement::GetBitShiftRadioButtonCheckedAnnouncement(announcement); +} diff --git a/src/CalcViewModel/StandardCalculatorViewModel.h b/src/CalcViewModel/StandardCalculatorViewModel.h index 909e32c9..d60122bf 100644 --- a/src/CalcViewModel/StandardCalculatorViewModel.h +++ b/src/CalcViewModel/StandardCalculatorViewModel.h @@ -12,11 +12,6 @@ #include "Common/BitLength.h" #include "Common/NumberBase.h" -namespace CalculatorFunctionalTests -{ - class HistoryTests; -} - namespace CalculatorUnitTests { class MultiWindowUnitTests; @@ -43,7 +38,6 @@ namespace CalculatorApp public: StandardCalculatorViewModel(); void UpdateOperand(int pos, Platform::String ^ text); - void UpdateCommandsInRecordingMode(); OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); OBSERVABLE_PROPERTY_RW(Platform::String ^, DisplayValue); @@ -71,7 +65,6 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_R(bool, IsUnaryOperatorEnabled); OBSERVABLE_PROPERTY_R(bool, IsNegateEnabled); OBSERVABLE_PROPERTY_RW(bool, IsDecimalEnabled); - OBSERVABLE_PROPERTY_R(bool, IsCurrentViewPinned); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IVector ^, MemorizedNumbers); OBSERVABLE_NAMED_PROPERTY_RW(bool, IsMemoryEmpty); OBSERVABLE_PROPERTY_R(bool, IsFToEChecked); @@ -83,8 +76,7 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_R(CalculatorApp::Common::NumberBase, CurrentRadixType); OBSERVABLE_PROPERTY_R(bool, AreTokensUpdated); OBSERVABLE_PROPERTY_R(bool, AreAlwaysOnTopResultsUpdated); - OBSERVABLE_PROPERTY_RW(bool, AreHistoryShortcutsEnabled); - OBSERVABLE_PROPERTY_R(bool, AreProgrammerRadixOperatorsEnabled); + OBSERVABLE_PROPERTY_R(bool, AreProgrammerRadixOperatorsVisible); OBSERVABLE_PROPERTY_R(bool, IsInputEmpty); OBSERVABLE_PROPERTY_R(CalculatorApp::Common::Automation::NarratorAnnouncement ^, Announcement); OBSERVABLE_PROPERTY_R(unsigned int, OpenParenthesisCount); @@ -111,7 +103,7 @@ namespace CalculatorApp { m_isBitFlipChecked = value; IsBinaryBitFlippingEnabled = IsProgrammer && m_isBitFlipChecked; - AreProgrammerRadixOperatorsEnabled = IsProgrammer && !m_isBitFlipChecked; + AreProgrammerRadixOperatorsVisible = IsProgrammer && !m_isBitFlipChecked; RaisePropertyChanged(L"IsBitFlipChecked"); } } @@ -186,7 +178,7 @@ namespace CalculatorApp IsBitFlipChecked = false; } IsBinaryBitFlippingEnabled = m_isProgrammer && IsBitFlipChecked; - AreProgrammerRadixOperatorsEnabled = m_isProgrammer && !IsBitFlipChecked; + AreProgrammerRadixOperatorsVisible = m_isProgrammer && !IsBitFlipChecked; if (value) { IsStandard = false; @@ -209,7 +201,6 @@ namespace CalculatorApp { if (m_isEditingEnabled != value) { - // Numbers::Common::KeyboardShortcutManager::IsCalculatorInEditingMode = value; m_isEditingEnabled = value; bool currentEditToggleValue = !m_isEditingEnabled; IsBinaryOperatorEnabled = currentEditToggleValue; @@ -249,7 +240,11 @@ namespace CalculatorApp } } - internal : + // Used by unit tests + void ResetCalcManager(bool clearMemory); + void SendCommandToCalcManager(int command); + + internal: void OnPaste(Platform::String ^ pastedString); void OnCopyCommand(Platform::Object ^ parameter); void OnPasteCommand(Platform::Object ^ parameter); @@ -262,7 +257,6 @@ namespace CalculatorApp void OnMemoryAdd(Platform::Object ^ memoryItemPosition); void OnMemorySubtract(Platform::Object ^ memoryItemPosition); void OnMemoryClear(_In_ Platform::Object ^ memoryItemPosition); - void OnPinUnpinCommand(Platform::Object ^ parameter); void OnInputChanged(); void DisplayPasteError(); @@ -273,6 +267,7 @@ namespace CalculatorApp void OnMaxDigitsReached(); void OnBinaryOperatorReceived(); void OnMemoryItemChanged(unsigned int indexOfMemory); + void SetBitshiftRadioButtonCheckedAnnouncement(Platform::String ^ announcement); Platform::String ^ GetLocalizedStringFormat(Platform::String ^ format, Platform::String ^ displayValue); void OnPropertyChanged(Platform::String ^ propertyname); @@ -310,7 +305,7 @@ namespace CalculatorApp _Inout_ std::shared_ptr>> const& commands); void SetTokens(_Inout_ std::shared_ptr>> const& tokens); NumbersAndOperatorsEnum ConvertIntegerToNumbersAndOperatorsEnum(unsigned int parameter); - static RADIX_TYPE GetRadixTypeFromNumberBase(CalculatorApp::Common::NumberBase base); + static RadixType GetRadixTypeFromNumberBase(CalculatorApp::Common::NumberBase base); NumbersAndOperatorsEnum m_CurrentAngleType; wchar_t m_decimalSeparator; CalculatorDisplay m_calculatorDisplay; @@ -332,7 +327,6 @@ namespace CalculatorApp Platform::String ^ m_localizedOpenParenthesisCountChangedAutomationFormat; Platform::String ^ m_localizedNoRightParenthesisAddedFormat; - bool m_pinned; bool m_isOperandEnabled; bool m_isEditingEnabled; bool m_isStandard; @@ -346,9 +340,9 @@ namespace CalculatorApp Platform::String ^ m_selectedExpressionLastData; Common::DisplayExpressionToken ^ m_selectedExpressionToken; - Platform::String ^ LocalizeDisplayValue(_In_ std::wstring const& displayValue, _In_ bool isError); + Platform::String ^ LocalizeDisplayValue(_In_ std::wstring const& displayValue); Platform::String - ^ CalculateNarratorDisplayValue(_In_ std::wstring const& displayValue, _In_ Platform::String ^ localizedDisplayValue, _In_ bool isError); + ^ CalculateNarratorDisplayValue(_In_ std::wstring const& displayValue, _In_ Platform::String ^ localizedDisplayValue); CalculatorApp::Common::Automation::NarratorAnnouncement ^ GetDisplayUpdatedNarratorAnnouncement(); Platform::String ^ GetCalculatorExpressionAutomationName(); Platform::String ^ GetNarratorStringReadRawNumbers(_In_ Platform::String ^ localizedDisplayValue); @@ -372,16 +366,11 @@ namespace CalculatorApp bool IsOpnd(CalculationManager::Command command); bool IsRecoverableCommand(CalculationManager::Command command); - CalculationManager::CommandType GetSelectedTokenType(_In_ unsigned int); void SaveEditedCommand(_In_ unsigned int index, _In_ CalculationManager::Command command); - 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 bab3c786..90960eb8 100644 --- a/src/CalcViewModel/UnitConverterViewModel.cpp +++ b/src/CalcViewModel/UnitConverterViewModel.cpp @@ -134,7 +134,6 @@ UnitConverterViewModel::UnitConverterViewModel(const shared_ptrIsGrouped = true; m_currencyFormatter->Mode = CurrencyFormatterMode::UseCurrencyCode; m_currencyFormatter->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown); - m_currencyMaxFractionDigits = m_currencyFormatter->FractionDigits; auto resourceLoader = AppResourceProvider::GetInstance(); m_localizedValueFromFormat = resourceLoader->GetResourceString(UnitConverterResourceKeys::ValueFromFormat); @@ -171,9 +170,6 @@ void UnitConverterViewModel::OnCategoryChanged(Object ^ parameter) void UnitConverterViewModel::ResetCategory() { - UCM::Category currentCategory = CurrentCategory->GetModelCategory(); - IsCurrencyCurrentCategory = currentCategory.id == NavCategory::Serialize(ViewMode::Currency); - m_isInputBlocked = false; SetSelectedUnits(); @@ -231,7 +227,9 @@ void UnitConverterViewModel::OnUnitChanged(Object ^ parameter) return; } + UpdateCurrencyFormatter(); m_model->SetCurrentUnitTypes(UnitFrom->GetModelUnit(), UnitTo->GetModelUnit()); + if (m_supplementaryResultsTimer != nullptr) { // End timer to show results immediately @@ -249,7 +247,7 @@ void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused) if (m_relocalizeStringOnSwitch) { // clean up any ill-formed strings that were in progress before the switch - ValueFrom = ConvertToLocalizedString(m_valueFromUnlocalized, false); + ValueFrom = ConvertToLocalizedString(m_valueFromUnlocalized, false, CurrencyFormatterParameterFrom); } SwitchConversionParameters(); @@ -264,17 +262,19 @@ void UnitConverterViewModel::OnSwitchActive(Platform::Object ^ unused) } m_valueFromUnlocalized.swap(m_valueToUnlocalized); - Utils::Swap(&m_localizedValueFromFormat, &m_localizedValueToFormat); + swap(m_localizedValueFromFormat, m_localizedValueToFormat); - Utils::Swap(&m_Unit1AutomationName, &m_Unit2AutomationName); + swap(m_Unit1AutomationName, m_Unit2AutomationName); RaisePropertyChanged(Unit1AutomationNamePropertyName); RaisePropertyChanged(Unit2AutomationNamePropertyName); m_isInputBlocked = false; m_model->SwitchActive(m_valueFromUnlocalized); + + UpdateIsDecimalEnabled(); } -String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings) +String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings, CurrencyFormatterParameter cfp) { Platform::String ^ result; @@ -283,10 +283,33 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st return result; } + CurrencyFormatter ^ currencyFormatter; + + switch (cfp) + { + case CurrencyFormatterParameter::ForValue1: + currencyFormatter = m_currencyFormatter1; + break; + case CurrencyFormatterParameter::ForValue2: + currencyFormatter = m_currencyFormatter2; + break; + default: + currencyFormatter = m_currencyFormatter; + break; + } + + // If unit hasn't been set, currencyFormatter1/2 is nullptr. Fallback to default. + if (currencyFormatter == nullptr) + { + currencyFormatter = m_currencyFormatter; + } + + int lastCurrencyFractionDigits = currencyFormatter->FractionDigits; + m_decimalFormatter->IsDecimalPointAlwaysDisplayed = false; m_decimalFormatter->FractionDigits = 0; - m_currencyFormatter->IsDecimalPointAlwaysDisplayed = false; - m_currencyFormatter->FractionDigits = 0; + currencyFormatter->IsDecimalPointAlwaysDisplayed = false; + currencyFormatter->FractionDigits = 0; wstring::size_type posOfE = stringToLocalize.find(L'e'); if (posOfE != wstring::npos) @@ -296,7 +319,8 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st std::wstring significandStr(stringToLocalize.substr(0, posOfE)); std::wstring exponentStr(stringToLocalize.substr(posOfSign + 1, stringToLocalize.length() - posOfSign)); - result += ConvertToLocalizedString(significandStr, allowPartialStrings) + "e" + signOfE + ConvertToLocalizedString(exponentStr, allowPartialStrings); + result += ConvertToLocalizedString(significandStr, allowPartialStrings, cfp) + "e" + signOfE + + ConvertToLocalizedString(exponentStr, allowPartialStrings, cfp); } else { @@ -307,7 +331,7 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st if (hasDecimal) { - if (allowPartialStrings) + if (allowPartialStrings && lastCurrencyFractionDigits > 0) { // allow "in progress" strings, like "3." that occur during the composition of // a final number. Without this, when typing the three characters in "3.2" @@ -315,18 +339,18 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st // typed a post-decimal digit. m_decimalFormatter->IsDecimalPointAlwaysDisplayed = true; - m_currencyFormatter->IsDecimalPointAlwaysDisplayed = true; + currencyFormatter->IsDecimalPointAlwaysDisplayed = true; } // force post-decimal digits so that trailing zeroes entered by the user aren't suddenly cut off. m_decimalFormatter->FractionDigits = static_cast(stringToLocalize.length() - (posOfDecimal + 1)); - m_currencyFormatter->FractionDigits = m_currencyMaxFractionDigits; + currencyFormatter->FractionDigits = lastCurrencyFractionDigits; } if (IsCurrencyCurrentCategory) { - wstring currencyResult = m_currencyFormatter->Format(stod(stringToLocalize))->Data(); - wstring currencyCode = m_currencyFormatter->Currency->Data(); + wstring currencyResult = currencyFormatter->Format(stod(stringToLocalize))->Data(); + wstring currencyCode = currencyFormatter->Currency->Data(); // CurrencyFormatter always includes LangCode or Symbol. Make it include LangCode // because this includes a non-breaking space. Remove the LangCode. @@ -384,7 +408,10 @@ String ^ UnitConverterViewModel::ConvertToLocalizedString(const std::wstring& st } result = L"-" + result; } - result = Utils::LRE + result + Utils::PDF; + + // restore the original fraction digits + currencyFormatter->FractionDigits = lastCurrencyFractionDigits; + return result; } @@ -398,9 +425,9 @@ void UnitConverterViewModel::DisplayPasteError() void UnitConverterViewModel::UpdateDisplay(const wstring& from, const wstring& to) { - String ^ fromStr = this->ConvertToLocalizedString(from, true); + String ^ fromStr = this->ConvertToLocalizedString(from, true, CurrencyFormatterParameterFrom); UpdateInputBlocked(from); - String ^ toStr = this->ConvertToLocalizedString(to, true); + String ^ toStr = this->ConvertToLocalizedString(to, true, CurrencyFormatterParameterTo); bool updatedValueFrom = ValueFrom != fromStr; bool updatedValueTo = ValueTo != toStr; @@ -477,21 +504,13 @@ void UnitConverterViewModel::OnButtonPressed(Platform::Object ^ parameter) } static constexpr UCM::Command OPERANDS[] = { UCM::Command::Zero, UCM::Command::One, UCM::Command::Two, UCM::Command::Three, UCM::Command::Four, - UCM::Command::Five, UCM::Command::Six, UCM::Command::Seven, UCM::Command::Eight, UCM::Command::Nine }; + UCM::Command::Five, UCM::Command::Six, UCM::Command::Seven, UCM::Command::Eight, UCM::Command::Nine }; - if (find(begin(OPERANDS), end(OPERANDS), command) != end(OPERANDS)) + // input should be allowed if user just switches active, because we will clear values in such cases + if (m_isInputBlocked && !m_model->IsSwitchedActive() && command != UCM::Command::Clear && command != UCM::Command::Backspace) { - if (m_isInputBlocked) - { - return; - } - - if (m_IsCurrencyCurrentCategory) - { - StartConversionResultTimer(); - } + return; } - m_model->SendCommand(command); TraceLogger::GetInstance()->LogConverterInputReceived(Mode); @@ -767,8 +786,8 @@ void UnitConverterViewModel::RefreshSupplementaryResults() for (tuple suggestedValue : m_cachedSuggestedValues) { - SupplementaryResult ^ result = - ref new SupplementaryResult(this->ConvertToLocalizedString(get<0>(suggestedValue), false), ref new Unit(get<1>(suggestedValue))); + SupplementaryResult ^ result = ref new SupplementaryResult( + this->ConvertToLocalizedString(get<0>(suggestedValue), false, CurrencyFormatterParameter::Default), ref new Unit(get<1>(suggestedValue))); if (result->IsWhimsical()) { whimsicals.push_back(result); @@ -815,10 +834,46 @@ void UnitConverterViewModel::UpdateInputBlocked(_In_ const wstring& currencyInpu m_isInputBlocked = false; if (posOfDecimal != wstring::npos && IsCurrencyCurrentCategory) { - m_isInputBlocked = (posOfDecimal + static_cast(m_currencyMaxFractionDigits) + 1 == currencyInput.length()); + m_isInputBlocked = (posOfDecimal + static_cast(CurrencyFormatterFrom->FractionDigits) + 1 == currencyInput.length()); } } +std::wstring TruncateFractionDigits(const std::wstring& n, int digitCount) +{ + auto i = n.find('.'); + if (i == std::wstring::npos) + return n; + size_t actualDigitCount = n.size() - i - 1; + return n.substr(0, n.size() - (actualDigitCount - digitCount)); +} + +void UnitConverterViewModel::UpdateCurrencyFormatter() +{ + if (!IsCurrencyCurrentCategory || m_Unit1->Abbreviation->IsEmpty() || m_Unit2->Abbreviation->IsEmpty()) + return; + + m_currencyFormatter1 = ref new CurrencyFormatter(m_Unit1->Abbreviation); + m_currencyFormatter1->IsGrouped = true; + m_currencyFormatter1->Mode = CurrencyFormatterMode::UseCurrencyCode; + m_currencyFormatter1->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown); + + m_currencyFormatter2 = ref new CurrencyFormatter(m_Unit2->Abbreviation); + m_currencyFormatter2->IsGrouped = true; + m_currencyFormatter2->Mode = CurrencyFormatterMode::UseCurrencyCode; + m_currencyFormatter2->ApplyRoundingForCurrency(RoundingAlgorithm::RoundHalfDown); + + UpdateIsDecimalEnabled(); + + OnPaste(ref new String(TruncateFractionDigits(m_valueFromUnlocalized, CurrencyFormatterFrom->FractionDigits).data())); +} + +void UnitConverterViewModel::UpdateIsDecimalEnabled() +{ + if (!IsCurrencyCurrentCategory || CurrencyFormatterFrom == nullptr) + return; + IsDecimalEnabled = CurrencyFormatterFrom->FractionDigits > 0; +} + NumbersAndOperatorsEnum UnitConverterViewModel::MapCharacterToButtonId(const wchar_t ch, bool& canSendNegate) { static_assert(NumbersAndOperatorsEnum::Zero < NumbersAndOperatorsEnum::One, "NumbersAndOperatorsEnum order is invalid"); @@ -946,14 +1001,19 @@ void UnitConverterViewModel::OnPaste(String ^ stringToPaste) } } -String ^ UnitConverterViewModel::GetLocalizedAutomationName(_In_ String ^ displayvalue, _In_ String ^ unitname, _In_ String ^ format) +String + ^ UnitConverterViewModel::GetLocalizedAutomationName( + _In_ String ^ displayvalue, + _In_ String ^ unitname, + _In_ String ^ format, + _In_ CurrencyFormatterParameter cfp) { String ^ valueToLocalize = displayvalue; if (displayvalue == ValueFrom && Utils::IsLastCharacterTarget(m_valueFromUnlocalized, m_decimalSeparator)) { // Need to compute a second localized value for the automation // name that does not include the decimal separator. - displayvalue = ConvertToLocalizedString(m_valueFromUnlocalized, false /*allowTrailingDecimal*/); + displayvalue = ConvertToLocalizedString(m_valueFromUnlocalized, false /*allowTrailingDecimal*/, cfp); format = m_localizedValueFromDecimalFormat; } @@ -974,7 +1034,7 @@ void UnitConverterViewModel::UpdateValue1AutomationName() { if (Unit1) { - Value1AutomationName = GetLocalizedAutomationName(Value1, Unit1->AccessibleName, m_localizedValueFromFormat); + Value1AutomationName = GetLocalizedAutomationName(Value1, Unit1->AccessibleName, m_localizedValueFromFormat, CurrencyFormatterParameter::ForValue1); } } @@ -982,7 +1042,7 @@ void UnitConverterViewModel::UpdateValue2AutomationName() { if (Unit2) { - Value2AutomationName = GetLocalizedAutomationName(Value2, Unit2->AccessibleName, m_localizedValueToFormat); + Value2AutomationName = GetLocalizedAutomationName(Value2, Unit2->AccessibleName, m_localizedValueToFormat, CurrencyFormatterParameter::ForValue1); } } @@ -998,18 +1058,6 @@ bool UnitConverterViewModel::UnitsAreValid() return UnitFrom != nullptr && !UnitFrom->Abbreviation->IsEmpty() && UnitTo != nullptr && !UnitTo->Abbreviation->IsEmpty(); } -void UnitConverterViewModel::StartConversionResultTimer() -{ - auto that(this); - m_conversionResultTaskHelper = make_unique(CONVERSION_FINALIZED_DELAY_IN_MS, [that]() { - if (that->UnitsAreValid()) - { - String ^ valueFrom = that->m_Value1Active ? that->m_Value1 : that->m_Value2; - String ^ valueTo = that->m_Value1Active ? that->m_Value2 : that->m_Value1; - } - }); -} - String ^ SupplementaryResult::GetLocalizedAutomationName() { auto format = AppResourceProvider::GetInstance()->GetResourceString("SupplementaryUnit_AutomationName"); diff --git a/src/CalcViewModel/UnitConverterViewModel.h b/src/CalcViewModel/UnitConverterViewModel.h index 6a8761a3..b48c1c3b 100644 --- a/src/CalcViewModel/UnitConverterViewModel.h +++ b/src/CalcViewModel/UnitConverterViewModel.h @@ -7,7 +7,6 @@ #include "Common/Utils.h" #include "Common/NetworkManager.h" #include "Common/Automation/NarratorAnnouncement.h" -#include "Common/ConversionResultTaskHelper.h" #include "Common/CalculatorButtonUser.h" #include "Common/NavCategory.h" @@ -144,7 +143,6 @@ namespace CalculatorApp OBSERVABLE_OBJECT_CALLBACK(OnPropertyChanged); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Categories); - OBSERVABLE_PROPERTY_RW(Category ^, CurrentCategory); OBSERVABLE_PROPERTY_RW(CalculatorApp::Common::ViewMode, Mode); OBSERVABLE_PROPERTY_R(Windows::Foundation::Collections::IObservableVector ^, Units); OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencySymbol1); @@ -165,7 +163,7 @@ namespace CalculatorApp OBSERVABLE_PROPERTY_RW(bool, IsDropDownOpen); OBSERVABLE_PROPERTY_RW(bool, IsDropDownEnabled); OBSERVABLE_NAMED_PROPERTY_RW(bool, IsCurrencyLoadingVisible); - OBSERVABLE_PROPERTY_RW(bool, IsCurrencyCurrentCategory); + OBSERVABLE_NAMED_PROPERTY_R(bool, IsCurrencyCurrentCategory); OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyRatioEquality); OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyRatioEqualityAutomationName); OBSERVABLE_PROPERTY_RW(Platform::String ^, CurrencyTimestamp); @@ -173,6 +171,26 @@ namespace CalculatorApp OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataLoadFailed); OBSERVABLE_NAMED_PROPERTY_RW(bool, CurrencyDataIsWeekOld); + public: + property Category ^ CurrentCategory + { + Category ^ get() { return m_CurrentCategory; } + void set(Category ^ value) + { + if (m_CurrentCategory == value) + { + return; + } + m_CurrentCategory = value; + if (value != nullptr) + { + auto currentCategory = value->GetModelCategory(); + IsCurrencyCurrentCategory = currentCategory.id == CalculatorApp::Common::NavCategory::Serialize(CalculatorApp::Common::ViewMode::Currency); + } + RaisePropertyChanged("CurrentCategory"); + } + } + property Windows::UI::Xaml::Visibility SupplementaryVisibility { Windows::UI::Xaml::Visibility get() @@ -209,8 +227,19 @@ namespace CalculatorApp void OnCopyCommand(Platform::Object ^ parameter); void OnPasteCommand(Platform::Object ^ parameter); + enum class CurrencyFormatterParameter + { + Default, + ForValue1, + ForValue2, + }; + Platform::String - ^ GetLocalizedAutomationName(_In_ Platform::String ^ displayvalue, _In_ Platform::String ^ unitname, _In_ Platform::String ^ format); + ^ GetLocalizedAutomationName( + _In_ Platform::String ^ displayvalue, + _In_ Platform::String ^ unitname, + _In_ Platform::String ^ format, + _In_ CurrencyFormatterParameter cfp); Platform::String ^ GetLocalizedConversionResultStringFormat( _In_ Platform::String ^ fromValue, @@ -258,13 +287,13 @@ namespace CalculatorApp void SupplementaryResultsTimerCancel(Windows::System::Threading::ThreadPoolTimer ^ timer); void RefreshSupplementaryResults(); void UpdateInputBlocked(_In_ const std::wstring& currencyInput); + void UpdateCurrencyFormatter(); + void UpdateIsDecimalEnabled(); bool UnitsAreValid(); void ResetCategory(); void OnButtonPressed(Platform::Object ^ parameter); - Platform::String ^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings); - - void StartConversionResultTimer(); + Platform::String ^ ConvertToLocalizedString(const std::wstring& stringToLocalize, bool allowPartialStrings, CurrencyFormatterParameter cfp); std::shared_ptr m_model; wchar_t m_decimalSeparator; @@ -274,6 +303,34 @@ namespace CalculatorApp Source, Target } m_value1cp; + property CurrencyFormatterParameter CurrencyFormatterParameterFrom + { + CurrencyFormatterParameter get() + { + return m_value1cp == ConversionParameter::Source ? CurrencyFormatterParameter::ForValue1 : CurrencyFormatterParameter::ForValue2; + } + } + property CurrencyFormatterParameter CurrencyFormatterParameterTo + { + CurrencyFormatterParameter get() + { + return m_value1cp == ConversionParameter::Target ? CurrencyFormatterParameter::ForValue1 : CurrencyFormatterParameter::ForValue2; + } + } + property Windows::Globalization::NumberFormatting::CurrencyFormatter^ CurrencyFormatterFrom + { + Windows::Globalization::NumberFormatting::CurrencyFormatter^ get() + { + return m_value1cp == ConversionParameter::Source ? m_currencyFormatter1 : m_currencyFormatter2; + } + } + property Windows::Globalization::NumberFormatting::CurrencyFormatter^ CurrencyFormatterTo + { + Windows::Globalization::NumberFormatting::CurrencyFormatter^ get() + { + return m_value1cp == ConversionParameter::Target ? m_currencyFormatter1 : m_currencyFormatter2; + } + } property Platform::String^ ValueFrom { Platform::String^ get() { return m_value1cp == ConversionParameter::Source ? Value1 : Value2; } @@ -307,7 +364,8 @@ namespace CalculatorApp std::mutex m_cacheMutex; Windows::Globalization::NumberFormatting::DecimalFormatter ^ m_decimalFormatter; Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter; - int m_currencyMaxFractionDigits; + Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter1; + Windows::Globalization::NumberFormatting::CurrencyFormatter ^ m_currencyFormatter2; std::wstring m_valueFromUnlocalized; std::wstring m_valueToUnlocalized; bool m_relocalizeStringOnSwitch; @@ -324,10 +382,8 @@ namespace CalculatorApp std::wstring m_lastAnnouncedFrom; std::wstring m_lastAnnouncedTo; Platform::String ^ m_lastAnnouncedConversionResult; - + Category ^ m_CurrentCategory; bool m_isCurrencyDataLoaded; - - std::unique_ptr m_conversionResultTaskHelper; }; class UnitConverterVMCallback : public UnitConversionManager::IUnitConverterVMCallback diff --git a/src/CalcViewModel/ViewState.cpp b/src/CalcViewModel/ViewState.cpp new file mode 100644 index 00000000..63a5d30e --- /dev/null +++ b/src/CalcViewModel/ViewState.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "ViewState.h" + +namespace CalculatorApp +{ + namespace ViewState + { + Platform::StringReference Snap(L"Snap"); + Platform::StringReference DockedView(L"DockedView"); + + bool IsValidViewState(Platform::String ^ viewState) + { + return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView); + } + } +} diff --git a/src/CalcViewModel/ViewState.h b/src/CalcViewModel/ViewState.h new file mode 100644 index 00000000..0a103d09 --- /dev/null +++ b/src/CalcViewModel/ViewState.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +namespace CalculatorApp +{ + namespace ViewState + { + extern Platform::StringReference Snap; + extern Platform::StringReference DockedView; + + bool IsValidViewState(Platform::String ^ viewState); + } +} diff --git a/src/CalcViewModel/packages.config b/src/CalcViewModel/packages.config deleted file mode 100644 index c148e5d7..00000000 --- a/src/CalcViewModel/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/CalcViewModel/pch.h b/src/CalcViewModel/pch.h index 77e1094b..da6ea470 100644 --- a/src/CalcViewModel/pch.h +++ b/src/CalcViewModel/pch.h @@ -40,6 +40,8 @@ #include "winrt/Windows.Globalization.DateTimeFormatting.h" #include "winrt/Windows.System.UserProfile.h" #include "winrt/Windows.UI.Xaml.h" +#include "winrt/Windows.Foundation.Metadata.h" +#include "winrt/Windows.Management.Policies.h" // The following namespaces exist as a convenience to resolve // ambiguity for Windows types in the Windows::UI::Xaml::Automation::Peers diff --git a/src/Calculator.sln b/src/Calculator.sln index aef0d881..09fc81fd 100644 --- a/src/Calculator.sln +++ b/src/Calculator.sln @@ -3,16 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29009.5 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Calculator", "Calculator\Calculator.vcxproj", "{9447424A-0E05-4911-BEB8-E0354405F39A}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcManager", "CalcManager\CalcManager.vcxproj", "{311E866D-8B93-4609-A691-265941FEE101}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3A5DF651-B8A1-45CA-9135-964A6FC7F5D1}" ProjectSection(SolutionItems) = preProject ..\.clang-format = ..\.clang-format nuget.config = nuget.config EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Calculator", "Calculator\Calculator.vcxproj", "{9447424A-0E05-4911-BEB8-E0354405F39A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcManager", "CalcManager\CalcManager.vcxproj", "{311E866D-8B93-4609-A691-265941FEE101}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalcViewModel", "CalcViewModel\CalcViewModel.vcxproj", "{90E9761D-9262-4773-942D-CAEAE75D7140}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CalculatorUnitTests", "CalculatorUnitTests\CalculatorUnitTests.vcxproj", "{D3BAED2C-4B07-4E1D-8807-9D6499450349}" @@ -21,6 +21,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITests", "Calcul EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CalculatorUITestFramework", "CalculatorUITestFramework\CalculatorUITestFramework.csproj", "{96454213-94AF-457D-9DF9-B14F80E7770F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphingImpl", "GraphingImpl\GraphingImpl.vcxproj", "{52E03A58-B378-4F50-8BFB-F659FB85E790}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GraphControl", "GraphControl\GraphControl.vcxproj", "{E727A92B-F149-492C-8117-C039A298719B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TraceLogging", "TraceLogging\TraceLogging.vcxproj", "{FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -137,10 +143,61 @@ Global {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x64.Build.0 = Release|Any CPU {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.ActiveCfg = Release|Any CPU {96454213-94AF-457D-9DF9-B14F80E7770F}.Release|x86.Build.0 = Release|Any CPU + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.ActiveCfg = Debug|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM.Build.0 = Debug|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|ARM64.Build.0 = Debug|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.ActiveCfg = Debug|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x64.Build.0 = Debug|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.ActiveCfg = Debug|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Debug|x86.Build.0 = Debug|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.ActiveCfg = Release|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM.Build.0 = Release|ARM + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.ActiveCfg = Release|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|ARM64.Build.0 = Release|ARM64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.ActiveCfg = Release|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x64.Build.0 = Release|x64 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.ActiveCfg = Release|Win32 + {52E03A58-B378-4F50-8BFB-F659FB85E790}.Release|x86.Build.0 = Release|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.ActiveCfg = Debug|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM.Build.0 = Debug|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|ARM64.Build.0 = Debug|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.ActiveCfg = Debug|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x64.Build.0 = Debug|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.ActiveCfg = Debug|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Debug|x86.Build.0 = Debug|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.ActiveCfg = Release|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM.Build.0 = Release|ARM + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.ActiveCfg = Release|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|ARM64.Build.0 = Release|ARM64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x64.ActiveCfg = Release|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x64.Build.0 = Release|x64 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x86.ActiveCfg = Release|Win32 + {E727A92B-F149-492C-8117-C039A298719B}.Release|x86.Build.0 = Release|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM.ActiveCfg = Debug|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM.Build.0 = Debug|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|ARM64.Build.0 = Debug|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x64.ActiveCfg = Debug|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x64.Build.0 = Debug|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x86.ActiveCfg = Debug|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Debug|x86.Build.0 = Debug|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM.ActiveCfg = Release|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM.Build.0 = Release|ARM + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM64.ActiveCfg = Release|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|ARM64.Build.0 = Release|ARM64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x64.ActiveCfg = Release|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x64.Build.0 = Release|x64 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x86.ActiveCfg = Release|Win32 + {FC81FF41-02CD-4CD9-9BC5-45A1E39AC6ED}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {311E866D-8B93-4609-A691-265941FEE101} = {3A5DF651-B8A1-45CA-9135-964A6FC7F5D1} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0EA53F83-5FA6-46A1-A290-A3C6962D2CAC} EndGlobalSection diff --git a/src/Calculator/AboutFlyout.xaml b/src/Calculator/AboutFlyout.xaml index 50f6f4e1..b83dfd83 100644 --- a/src/Calculator/AboutFlyout.xaml +++ b/src/Calculator/AboutFlyout.xaml @@ -14,6 +14,7 @@ + @@ -72,5 +73,21 @@ Click="FeedbackButton_Click"/> + + + + + + + diff --git a/src/Calculator/AboutFlyout.xaml.cpp b/src/Calculator/AboutFlyout.xaml.cpp index 5f295c59..9a84a752 100644 --- a/src/Calculator/AboutFlyout.xaml.cpp +++ b/src/Calculator/AboutFlyout.xaml.cpp @@ -40,6 +40,8 @@ AboutFlyout::AboutFlyout() auto copyrightText = LocalizationStringUtil::GetLocalizedString(resourceLoader->GetResourceString("AboutControlCopyright"), StringReference(to_wstring(BUILD_YEAR).c_str())); AboutControlCopyrightRun->Text = copyrightText; + + InitializeContributeTextBlock(); } void AboutFlyout::FeedbackButton_Click(_In_ Object ^ sender, _In_ RoutedEventArgs ^ e) @@ -61,3 +63,30 @@ void AboutFlyout::SetDefaultFocus() { AboutFlyoutEULA->Focus(::FocusState::Programmatic); } + +void AboutFlyout::InitializeContributeTextBlock() +{ + auto resProvider = AppResourceProvider::GetInstance(); + std::wstring contributeHyperlinkText = resProvider->GetResourceString(L"AboutFlyoutContribute")->Data(); + + // The resource string has the 'GitHub' hyperlink wrapped with '%HL%'. + // Break the string and assign pieces appropriately. + static const std::wstring delimiter{ L"%HL%" }; + static const size_t delimiterLength{ delimiter.length() }; + + // Find the delimiters. + size_t firstSplitPosition = contributeHyperlinkText.find(delimiter, 0); + assert(firstSplitPosition != std::wstring::npos); + size_t secondSplitPosition = contributeHyperlinkText.find(delimiter, firstSplitPosition + 1); + assert(secondSplitPosition != std::wstring::npos); + size_t hyperlinkTextLength = secondSplitPosition - (firstSplitPosition + delimiterLength); + + // Assign pieces. + auto contributeTextBeforeHyperlink = ref new String(contributeHyperlinkText.substr(0, firstSplitPosition).c_str()); + auto contributeTextLink = ref new String(contributeHyperlinkText.substr(firstSplitPosition + delimiterLength, hyperlinkTextLength).c_str()); + auto contributeTextAfterHyperlink = ref new String(contributeHyperlinkText.substr(secondSplitPosition + delimiterLength).c_str()); + + ContributeRunBeforeLink->Text = contributeTextBeforeHyperlink; + ContributeRunLink->Text = contributeTextLink; + ContributeRunAfterLink->Text = contributeTextAfterHyperlink; +} diff --git a/src/Calculator/AboutFlyout.xaml.h b/src/Calculator/AboutFlyout.xaml.h index ab0e481e..f4e47a36 100644 --- a/src/Calculator/AboutFlyout.xaml.h +++ b/src/Calculator/AboutFlyout.xaml.h @@ -18,5 +18,6 @@ public private: void FeedbackButton_Click(_In_ Platform::Object ^ sender, _In_ Windows::UI::Xaml::RoutedEventArgs ^ e); void SetVersionString(); + void InitializeContributeTextBlock(); }; } /* namespace CalculatorApp */ diff --git a/src/Calculator/App.xaml b/src/Calculator/App.xaml index f27dcdf8..4a0cca57 100644 --- a/src/Calculator/App.xaml +++ b/src/Calculator/App.xaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Controls="using:CalculatorApp.Controls" xmlns:common="using:CalculatorApp.Common" - xmlns:converters="using:CalculatorApp.Converters" xmlns:local="using:CalculatorApp"> @@ -28,8 +27,12 @@ Opacity="0.4" Color="{ThemeResource SystemAltMediumLowColor}"/> - - + + @@ -44,6 +47,7 @@ Opacity="0.9" Color="{ThemeResource SystemAccentColor}"/> + + Dark + + + + + + + + + + + + + + + + + 0,0,0,0 @@ -80,11 +102,16 @@ Opacity="0.4" Color="{ThemeResource SystemAltMediumLowColor}"/> - - + + + + Light + + + + + + + + + + + + + + + + + 0,1,0,0 @@ -136,6 +181,7 @@ + @@ -146,6 +192,13 @@ + + Dark + + + + + @@ -196,6 +249,10 @@ 14 16 + + + + + + + + + + + + + diff --git a/src/Calculator/App.xaml.cpp b/src/Calculator/App.xaml.cpp index 2936680d..b8f7702d 100644 --- a/src/Calculator/App.xaml.cpp +++ b/src/Calculator/App.xaml.cpp @@ -148,6 +148,10 @@ task App::SetupJumpList() for (NavCategory ^ option : calculatorOptions->Categories) { + if (!option->IsEnabled) + { + continue; + } ViewMode mode = option->Mode; auto item = JumpListItem::CreateWithArguments(((int)mode).ToString(), L"ms-resource:///Resources/" + NavCategory::GetNameResourceKey(mode)); item->Description = L"ms-resource:///Resources/" + NavCategory::GetNameResourceKey(mode); diff --git a/src/Calculator/Assets/CalcMDL2.ttf b/src/Calculator/Assets/CalcMDL2.ttf index 9e6bfaa4..9ee5deb0 100644 Binary files a/src/Calculator/Assets/CalcMDL2.ttf and b/src/Calculator/Assets/CalcMDL2.ttf differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorAppList.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorAppList.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorAppList.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorAppList.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorAppList.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorAppList.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorAppList.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorAppList.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorAppList.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorAppList.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorAppList.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorAppList.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorAppList.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorAppList.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorAppList.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorAppList.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorAppList.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorAppList.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorAppList.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorAppList.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-16.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-16.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-16.png b/src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-16.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-16_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-20.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-20.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-20.png b/src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-20.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-20_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-24.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-24.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-24.png b/src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-24.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-24_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-lightunplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-lightunplated_devicefamily-colorfulunplated.png deleted file mode 100644 index c0fff46a..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-lightunplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-black_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-black_devicefamily-colorfulunplated.png deleted file mode 100644 index acb3f81e..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-black_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-white_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-white_devicefamily-colorfulunplated.png deleted file mode 100644 index c0fff46a..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_contrast-white_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_devicefamily-colorfulunplated.png deleted file mode 100644 index c0fff46a..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-256_altform-unplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-256.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-256.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-256.png b/src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-256.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-256_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-30.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-30.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-30.png b/src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-30.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-30_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-32.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-32.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-32.png b/src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-32.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-32_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-36.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-36.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-36.png b/src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-36.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-36_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-40.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-40.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-40.png b/src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-40.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-40_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-lightunplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-lightunplated_devicefamily-colorfulunplated.png deleted file mode 100644 index 0aea56fd..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-lightunplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-black_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-black_devicefamily-colorfulunplated.png deleted file mode 100644 index b801ca63..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-black_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-white_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-white_devicefamily-colorfulunplated.png deleted file mode 100644 index 0aea56fd..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_contrast-white_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_devicefamily-colorfulunplated.png deleted file mode 100644 index 0aea56fd..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-48_altform-unplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-48.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-48.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-48.png b/src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-48.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-48_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-60.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-60.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-60.png b/src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-60.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-60_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-64.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-64.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-64.png b/src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-64.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-64_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-72.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-72.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-72.png b/src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-72.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-72_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-80.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-80.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-80.png b/src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-80.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-80_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-lightunplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-lightunplated_devicefamily-colorfulunplated.png deleted file mode 100644 index 3ec68f62..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-lightunplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-black_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-black_devicefamily-colorfulunplated.png deleted file mode 100644 index 3bea4ce1..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-black_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-white_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-white_devicefamily-colorfulunplated.png deleted file mode 100644 index 3ec68f62..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_contrast-white_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_devicefamily-colorfulunplated.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_devicefamily-colorfulunplated.png deleted file mode 100644 index 3ec68f62..00000000 Binary files a/src/Calculator/Assets/CalculatorAppList.targetsize-96_altform-unplated_devicefamily-colorfulunplated.png and /dev/null differ diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-96.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-black_targetsize-96.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-96.png b/src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorAppList.contrast-white_targetsize-96.png rename to src/Calculator/Assets/CalculatorAppList.targetsize-96_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorLargeTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorLargeTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorMedTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorMedTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorMedTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorMedTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorMedTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorMedTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorSmallTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorSmallTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100_altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100_altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-125_altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png new file mode 100644 index 00000000..d1f3b077 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-125_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-150_altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png new file mode 100644 index 00000000..dd35d6b2 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-150_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-200_altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png new file mode 100644 index 00000000..045d5cd2 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-200_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_altform-colorful.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-400_altform-colorful.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorSplashScreen.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png new file mode 100644 index 00000000..e9dd7b14 Binary files /dev/null and b/src/Calculator/Assets/CalculatorSplashScreen.scale-400_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-200.png b/src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorStoreLogo.contrast-white_scale-200.png rename to src/Calculator/Assets/CalculatorStoreLogo.scale-200_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-100.png b/src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-100.png rename to src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-100.png b/src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-100.png rename to src/Calculator/Assets/CalculatorWideTile.scale-100_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-125.png b/src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-125.png rename to src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-125.png b/src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-125.png rename to src/Calculator/Assets/CalculatorWideTile.scale-125_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-150.png b/src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-150.png rename to src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-150.png b/src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-150.png rename to src/Calculator/Assets/CalculatorWideTile.scale-150_contrast-white.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-200.png b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-200.png rename to src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png new file mode 100644 index 00000000..c09eb4b6 Binary files /dev/null and b/src/Calculator/Assets/CalculatorWideTile.scale-200_contrast-white.png differ diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-400.png b/src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-black.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-black_scale-400.png rename to src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-black.png diff --git a/src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-400.png b/src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-white.png similarity index 100% rename from src/Calculator/Assets/CalculatorWideTile.contrast-white_scale-400.png rename to src/Calculator/Assets/CalculatorWideTile.scale-400_contrast-white.png diff --git a/src/Calculator/Assets/Graphing.targetsize-16.png b/src/Calculator/Assets/Graphing.targetsize-16.png new file mode 100644 index 00000000..485e7349 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png new file mode 100644 index 00000000..485e7349 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png new file mode 100644 index 00000000..46788045 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-16_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20.png b/src/Calculator/Assets/Graphing.targetsize-20.png new file mode 100644 index 00000000..9b1754ed Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png new file mode 100644 index 00000000..9b1754ed Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png new file mode 100644 index 00000000..517e7413 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-20_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24.png b/src/Calculator/Assets/Graphing.targetsize-24.png new file mode 100644 index 00000000..2670817a Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png new file mode 100644 index 00000000..2670817a Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png new file mode 100644 index 00000000..f603fc85 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-24_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32.png b/src/Calculator/Assets/Graphing.targetsize-32.png new file mode 100644 index 00000000..b8ae7bb5 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png new file mode 100644 index 00000000..b8ae7bb5 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png new file mode 100644 index 00000000..8dd829ac Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-32_contrast-white.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64.png b/src/Calculator/Assets/Graphing.targetsize-64.png new file mode 100644 index 00000000..e347e193 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png b/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png new file mode 100644 index 00000000..e347e193 Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64_contrast-black.png differ diff --git a/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png b/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png new file mode 100644 index 00000000..d5cdc05b Binary files /dev/null and b/src/Calculator/Assets/Graphing.targetsize-64_contrast-white.png differ diff --git a/src/Calculator/Calculator.vcxproj b/src/Calculator/Calculator.vcxproj index 24908557..a96a7464 100644 --- a/src/Calculator/Calculator.vcxproj +++ b/src/Calculator/Calculator.vcxproj @@ -16,8 +16,8 @@ 10.0 black Always - TemporaryKey.pfx - true + WindowsDev_TemporaryKey.pfx + True False @@ -130,9 +130,12 @@ + + + - /bigobj /await /std:c++17 /utf-8 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -141,7 +144,7 @@ - /bigobj /await /std:c++17 /utf-8 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -174,7 +177,7 @@ - /bigobj /await /std:c++17 /utf-8 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -195,7 +198,7 @@ - /bigobj /await /std:c++17 /utf-8 + /bigobj /await /std:c++17 /utf-8 %(AdditionalOptions) 4453;28204 $(SolutionDir)..\src\;%(AdditionalIncludeDirectories) Level4 @@ -222,6 +225,9 @@ 0.0.0.0 + + true + @@ -230,10 +236,13 @@ AboutFlyout.xaml + + + + - @@ -249,10 +258,17 @@ + + App.xaml + + EquationStylePanelControl.xaml + + + Views\Calculator.xaml @@ -277,6 +293,21 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + + + Views\GraphingCalculator\GraphingSettings.xaml + + + Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml + + + Views\GraphingCalculator\GraphingNumPad.xaml + Views\HistoryList.xaml @@ -316,6 +347,9 @@ Designer + + Designer + Designer @@ -333,6 +367,11 @@ + + + + + @@ -351,9 +390,7 @@ Designer - - - + @@ -363,9 +400,11 @@ App.xaml + + + - @@ -381,6 +420,8 @@ + + Create Create @@ -391,6 +432,11 @@ Create Create + + EquationStylePanelControl.xaml + + + Views\Calculator.xaml @@ -415,6 +461,21 @@ Views\CalculatorStandardOperators.xaml + + Views\GraphingCalculator\EquationInputArea.xaml + + + Views\GraphingCalculator\GraphingCalculator.xaml + + + Views\GraphingCalculator\GraphingSettings.xaml + + + Views\GraphingCalculator\KeyGraphFeaturesPanel.xaml + + + Views\GraphingCalculator\GraphingNumPad.xaml + Views\HistoryList.xaml @@ -458,12 +519,8 @@ - - - - @@ -500,8 +557,6 @@ - - @@ -562,8 +617,6 @@ - - @@ -574,8 +627,6 @@ - - @@ -583,181 +634,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -768,6 +883,21 @@ + + + + + + + + + + + + + + + @@ -808,6 +938,9 @@ {90e9761d-9262-4773-942d-caeae75d7140} + + {e727a92b-f149-492c-8117-c039a298719b} + @@ -827,7 +960,7 @@ - + @@ -835,6 +968,6 @@ - + \ No newline at end of file diff --git a/src/Calculator/Calculator.vcxproj.filters b/src/Calculator/Calculator.vcxproj.filters index ee5ce92e..790f6623 100644 --- a/src/Calculator/Calculator.vcxproj.filters +++ b/src/Calculator/Calculator.vcxproj.filters @@ -218,6 +218,12 @@ {0120c344-0bc0-4a1d-b82c-df7945f46189} + + {e23e2a6e-491b-4200-9bf7-d355a1ee695b} + + + {b491a249-26b8-4814-9f50-2c3a57018c56} + @@ -225,9 +231,6 @@ - - Common - Controls @@ -308,6 +311,24 @@ Controls + + + + Controls + + + + Views\GraphingCalculator + + + + Utils + + + Common + + + @@ -315,9 +336,6 @@ Common - - Common - Controls @@ -398,13 +416,34 @@ Controls + + + + Controls + + + + Views\GraphingCalculator + + + + Utils + + + Common + + + Common + + + - + @@ -447,9 +486,6 @@ Views - - Views - Views @@ -465,6 +501,28 @@ Views + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + Views + + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + Views\GraphingCalculator + + + + Views\StateTriggers + @@ -476,9 +534,6 @@ Resources\az-Latn-az - - Resources\uz-latn-uz - Resources\af-za @@ -491,9 +546,6 @@ Resources\vi-vn - - Resources\be-by - Resources\am-et @@ -518,12 +570,6 @@ Resources\zh-cn - - Resources\sw-ke - - - Resources\bn-bd - Resources\ta-in @@ -614,9 +660,6 @@ Resources\pt-br - - Resources\ha-latn-ng - Resources\lt-lt @@ -749,9 +792,6 @@ Resources\ru-ru - - Resources\ha-latn-ng - Resources\lo-la @@ -794,9 +834,6 @@ Resources\el-gr - - Resources\sw-ke - Resources\uk-ua @@ -806,9 +843,6 @@ Resources\vi-vn - - Resources\uz-latn-uz - Resources\tr-tr @@ -818,9 +852,6 @@ Resources\zh-tw - - Resources\bn-bd - Resources\cs-cz @@ -842,9 +873,6 @@ Resources\ar-sa - - Resources\be-by - Resources\kk-kz @@ -859,450 +887,6 @@ - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - Assets @@ -1423,82 +1007,550 @@ Assets - + Assets - + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + Assets Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + Assets - + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + Assets @@ -1510,4 +1562,8 @@ - + + + + + \ No newline at end of file diff --git a/src/Calculator/Common/AppLifecycleLogger.cpp b/src/Calculator/Common/AppLifecycleLogger.cpp index 0df91361..bc4b760f 100644 --- a/src/Calculator/Common/AppLifecycleLogger.cpp +++ b/src/Calculator/Common/AppLifecycleLogger.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "AppLifecycleLogger.h" -#include "CalcViewModel/Common/TraceActivity.h" +#include using namespace std; using namespace winrt; diff --git a/src/Calculator/Common/BindableBase.cpp b/src/Calculator/Common/BindableBase.cpp deleted file mode 100644 index ccba7985..00000000 --- a/src/Calculator/Common/BindableBase.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "pch.h" -#include "BindableBase.h" - -using namespace CalculatorApp::Common; - -using namespace Platform; -using namespace Windows::UI::Xaml::Data; - -/// -/// Notifies listeners that a property value has changed. -/// -/// Name of the property used to notify listeners. -void BindableBase::OnPropertyChanged(String ^ propertyName) -{ - PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName)); -} - -Windows::UI::Xaml::Data::ICustomProperty ^ BindableBase::GetCustomProperty(Platform::String ^ name) -{ - return nullptr; -} - -Windows::UI::Xaml::Data::ICustomProperty ^ BindableBase::GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type) -{ - return nullptr; -} - -Platform::String ^ BindableBase::GetStringRepresentation() -{ - return this->ToString(); -} diff --git a/src/Calculator/Common/BindableBase.h b/src/Calculator/Common/BindableBase.h deleted file mode 100644 index d1f958f5..00000000 --- a/src/Calculator/Common/BindableBase.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -namespace CalculatorApp -{ - namespace Common - { - /// - /// Implementation of to simplify models. - /// - [Windows::Foundation::Metadata::WebHostHidden] public ref class BindableBase : Windows::UI::Xaml::DependencyObject, - Windows::UI::Xaml::Data::INotifyPropertyChanged, - Windows::UI::Xaml::Data::ICustomPropertyProvider - { - public: - virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler ^ PropertyChanged; - - public: - // ICustomPropertyProvider - virtual Windows::UI::Xaml::Data::ICustomProperty ^ GetCustomProperty(Platform::String ^ name); - virtual Windows::UI::Xaml::Data::ICustomProperty ^ GetIndexedProperty(Platform::String ^ name, Windows::UI::Xaml::Interop::TypeName type); - virtual Platform::String ^ GetStringRepresentation(); - - property Windows::UI::Xaml::Interop::TypeName Type - { - virtual Windows::UI::Xaml::Interop::TypeName get() - { - return this->GetType(); - } - } - - protected: - virtual void OnPropertyChanged(Platform::String ^ propertyName); - }; - } -} diff --git a/src/Calculator/Common/KeyboardShortcutManager.cpp b/src/Calculator/Common/KeyboardShortcutManager.cpp new file mode 100644 index 00000000..bed0c4b2 --- /dev/null +++ b/src/Calculator/Common/KeyboardShortcutManager.cpp @@ -0,0 +1,691 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "KeyboardShortcutManager.h" +#include "CalcViewModel/Common/AppResourceProvider.h" +#include "CalcViewModel/ApplicationViewModel.h" +#include "CalcViewModel/Common/LocalizationSettings.h" + +using namespace Concurrency; +using namespace Platform; +using namespace std; +using namespace chrono; +using namespace Windows::ApplicationModel::Resources; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Core; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::System; +using namespace Utils; +using namespace CalculatorApp; +using namespace CalculatorApp::Common; +using namespace CalculatorApp::ViewModel; + +namespace MUXC = Microsoft::UI::Xaml::Controls; + +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, Character); +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKey); +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlChord); +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyShiftChord); +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyAltChord); +DEPENDENCY_PROPERTY_INITIALIZATION(KeyboardShortcutManager, VirtualKeyControlShiftChord); + +map> KeyboardShortcutManager::s_characterForButtons; +map> KeyboardShortcutManager::s_virtualKey; +map> KeyboardShortcutManager::s_VirtualKeyControlChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyShiftChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyAltChordsForButtons; +map> KeyboardShortcutManager::s_VirtualKeyControlShiftChordsForButtons; + +map KeyboardShortcutManager::s_IsDropDownOpen; +map KeyboardShortcutManager::s_ignoreNextEscape; +map KeyboardShortcutManager::s_keepIgnoringEscape; +map KeyboardShortcutManager::s_fHonorShortcuts; +map KeyboardShortcutManager::s_fDisableShortcuts; +reader_writer_lock KeyboardShortcutManager::s_keyboardShortcutMapLock; + +namespace CalculatorApp +{ + namespace Common + { + // Lights up all of the buttons in the given range + // The range is defined by a pair of iterators + template + void LightUpButtons(const T& buttons) + { + auto iterator = buttons.first; + for (; iterator != buttons.second; ++iterator) + { + auto button = iterator->second.Resolve(); + if (button && button->IsEnabled) + { + LightUpButton(button); + } + } + } + + void LightUpButton(ButtonBase ^ button) + { + // If the button is a toggle button then we don't need + // to change the UI of the button + if (dynamic_cast(button)) + { + return; + } + + // The button will go into the visual Pressed state with this call + VisualStateManager::GoToState(button, "Pressed", true); + + // This timer will fire after lightUpTime and make the button + // go back to the normal state. + // This timer will only fire once after which it will be destroyed + auto timer = ref new DispatcherTimer(); + TimeSpan lightUpTime{}; + lightUpTime.Duration = 500000L; // Half second (in 100-ns units) + timer->Interval = lightUpTime; + + WeakReference timerWeakReference(timer); + WeakReference buttonWeakReference(button); + timer->Tick += ref new EventHandler([buttonWeakReference, timerWeakReference](Object ^, Object ^) { + auto button = buttonWeakReference.Resolve(); + if (button) + { + VisualStateManager::GoToState(button, "Normal", true); + } + + // Cancel the timer after we're done so it only fires once + auto timer = timerWeakReference.Resolve(); + if (timer) + { + timer->Stop(); + } + }); + timer->Start(); + } + + // Looks for the first button reference that it can resolve + // and execute its command. + // NOTE: It is assumed that all buttons associated with a particular + // key have the same command + template + void RunFirstEnabledButtonCommand(const T& buttons) + { + auto buttonIterator = buttons.first; + for (; buttonIterator != buttons.second; ++buttonIterator) + { + auto button = buttonIterator->second.Resolve(); + if (button && button->IsEnabled) + { + RunButtonCommand(button); + break; + } + } + } + + void RunButtonCommand(ButtonBase ^ button) + { + if (button->IsEnabled) + { + auto command = button->Command; + auto parameter = button->CommandParameter; + if (command && command->CanExecute(parameter)) + { + command->Execute(parameter); + } + + auto radio = dynamic_cast(button); + if (radio) + { + radio->IsChecked = true; + return; + } + + auto toggle = dynamic_cast(button); + if (toggle) + { + toggle->IsChecked = !(toggle->IsChecked != nullptr && toggle->IsChecked->Value); + return; + } + } + } + } +} + +void KeyboardShortcutManager::IgnoreEscape(bool onlyOnce) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + int viewId = Utils::GetWindowId(); + + if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end()) + { + s_ignoreNextEscape[viewId] = true; + } + + if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) + { + s_keepIgnoringEscape[viewId] = !onlyOnce; + } +} + +void KeyboardShortcutManager::HonorEscape() +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + int viewId = Utils::GetWindowId(); + + if (s_ignoreNextEscape.find(viewId) != s_ignoreNextEscape.end()) + { + s_ignoreNextEscape[viewId] = false; + } + + if (s_keepIgnoringEscape.find(viewId) != s_keepIgnoringEscape.end()) + { + s_keepIgnoringEscape[viewId] = false; + } +} + +void KeyboardShortcutManager::OnCharacterPropertyChanged(DependencyObject ^ target, String ^ oldValue, String ^ newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + auto button = safe_cast(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_characterForButtons.find(viewId); + + if (iterViewMap != s_characterForButtons.end()) + { + if (oldValue) + { + iterViewMap->second.erase(oldValue->Data()[0]); + } + + if (newValue) + { + if (newValue == L".") + { + wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator(); + iterViewMap->second.insert(make_pair(decSep, WeakReference(button))); + } + else + { + iterViewMap->second.insert(make_pair(newValue->Data()[0], WeakReference(button))); + } + } + } + else + { + s_characterForButtons.insert(make_pair(viewId, multimap())); + + if (newValue == L".") + { + wchar_t decSep = LocalizationSettings::GetInstance().GetDecimalSeparator(); + s_characterForButtons.find(viewId)->second.insert(make_pair(decSep, WeakReference(button))); + } + else + { + s_characterForButtons.find(viewId)->second.insert(make_pair(newValue->Data()[0], WeakReference(button))); + } + } +} + +void KeyboardShortcutManager::OnVirtualKeyPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + auto button = static_cast(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_virtualKey.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_virtualKey.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_virtualKey.insert(make_pair(viewId, multimap())); + s_virtualKey.find(viewId)->second.insert(make_pair(newValue, WeakReference(button))); + } +} + +void KeyboardShortcutManager::OnVirtualKeyControlChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + Control ^ control = dynamic_cast(target); + + if (control == nullptr) + { + // Handling Ctrl+E shortcut for Date Calc, target would be NavigationView^ in that case + control = safe_cast(target); + } + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_VirtualKeyControlChordsForButtons.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_VirtualKeyControlChordsForButtons.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(control))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyControlChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyControlChordsForButtons.find(viewId)->second.insert(make_pair(newValue, WeakReference(control))); + } +} + +void KeyboardShortcutManager::OnVirtualKeyShiftChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + auto button = safe_cast(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_VirtualKeyShiftChordsForButtons.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_VirtualKeyShiftChordsForButtons.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyShiftChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyShiftChordsForButtons.find(viewId)->second.insert(make_pair(newValue, WeakReference(button))); + } +} + +void KeyboardShortcutManager::OnVirtualKeyAltChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + MUXC::NavigationView ^ navView = safe_cast(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_VirtualKeyAltChordsForButtons.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_VirtualKeyAltChordsForButtons.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(navView))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyAltChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyAltChordsForButtons.find(viewId)->second.insert(make_pair(newValue, WeakReference(navView))); + } +} + +void KeyboardShortcutManager::OnVirtualKeyControlShiftChordPropertyChanged(DependencyObject ^ target, MyVirtualKey /*oldValue*/, MyVirtualKey newValue) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + auto button = safe_cast(target); + + int viewId = Utils::GetWindowId(); + auto iterViewMap = s_VirtualKeyControlShiftChordsForButtons.find(viewId); + + // Check if the View Id has already been registered + if (iterViewMap != s_VirtualKeyControlShiftChordsForButtons.end()) + { + iterViewMap->second.insert(make_pair(newValue, WeakReference(button))); + } + else + { + // If the View Id is not already registered, then register it and make the entry + s_VirtualKeyControlShiftChordsForButtons.insert(make_pair(viewId, multimap())); + s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second.insert(make_pair(newValue, WeakReference(button))); + } +} + +// In the three event handlers below we will not mark the event as handled +// because this is a supplemental operation and we don't want to interfere with +// the normal keyboard handling. +void KeyboardShortcutManager::OnCharacterReceivedHandler(CoreWindow ^ sender, CharacterReceivedEventArgs ^ args) +{ + int viewId = Utils::GetWindowId(); + auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId); + + if (currentHonorShortcuts == s_fHonorShortcuts.end() || currentHonorShortcuts->second) + { + wchar_t character = static_cast(args->KeyCode); + auto buttons = s_characterForButtons.find(viewId)->second.equal_range(character); + RunFirstEnabledButtonCommand(buttons); + + LightUpButtons(buttons); + } +} + +const multimap* KeyboardShortcutManager::GetCurrentKeyDictionary(bool controlKeyPressed, bool shiftKeyPressed, bool altPressed) +{ + int viewId = Utils::GetWindowId(); + + if (controlKeyPressed) + { + if (altPressed) + { + return nullptr; + } + else + { + if (shiftKeyPressed) + { + return &s_VirtualKeyControlShiftChordsForButtons.find(viewId)->second; + } + else + { + return &s_VirtualKeyControlChordsForButtons.find(viewId)->second; + } + } + } + else + { + if (altPressed) + { + if (!shiftKeyPressed) + { + return &s_VirtualKeyAltChordsForButtons.find(viewId)->second; + } + else + { + return nullptr; + } + } + else + { + if (shiftKeyPressed) + { + return &s_VirtualKeyShiftChordsForButtons.find(viewId)->second; + } + else + { + return &s_virtualKey.find(viewId)->second; + } + } + } +} + +void KeyboardShortcutManager::OnKeyDownHandler(CoreWindow ^ sender, KeyEventArgs ^ args) +{ + if (args->Handled) + { + return; + } + + auto key = args->VirtualKey; + int viewId = Utils::GetWindowId(); + + const bool isControlKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const bool isShiftKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const bool isAltKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + + // Handle Ctrl + E for DateCalculator + if ((key == VirtualKey::E) && isControlKeyPressed && !isShiftKeyPressed && !isAltKeyPressed) + { + const auto lookupMap = GetCurrentKeyDictionary(isControlKeyPressed, isShiftKeyPressed, false); + if (lookupMap == nullptr) + { + return; + } + + auto buttons = lookupMap->equal_range(static_cast(key)); + auto navView = buttons.first->second.Resolve(); + auto appViewModel = safe_cast(navView->DataContext); + appViewModel->Mode = ViewMode::Date; + auto categoryName = AppResourceProvider::GetInstance()->GetResourceString(L"DateCalculationModeText"); + appViewModel->CategoryName = categoryName; + + auto menuItems = static_cast ^>(navView->MenuItemsSource); + auto flatIndex = NavCategory::GetFlatIndex(ViewMode::Date); + navView->SelectedItem = menuItems->GetAt(flatIndex); + return; + } + + auto currentIgnoreNextEscape = s_ignoreNextEscape.find(viewId); + if (currentIgnoreNextEscape != s_ignoreNextEscape.end()) + { + if (currentIgnoreNextEscape->second && key == VirtualKey::Escape) + { + auto currentKeepIgnoringEscape = s_keepIgnoringEscape.find(viewId); + + if (currentKeepIgnoringEscape != s_keepIgnoringEscape.end()) + { + if (!currentKeepIgnoringEscape->second) + { + HonorEscape(); + } + return; + } + } + } + + auto currentHonorShortcuts = s_fHonorShortcuts.find(viewId); + if (currentHonorShortcuts != s_fHonorShortcuts.end()) + { + if (currentHonorShortcuts->second) + { + const auto myVirtualKey = static_cast(key); + const auto lookupMap = GetCurrentKeyDictionary(isControlKeyPressed, isShiftKeyPressed, isAltKeyPressed); + if (lookupMap == nullptr) + { + return; + } + + auto buttons = lookupMap->equal_range(myVirtualKey); + if (buttons.first == buttons.second) + { + return; + } + + RunFirstEnabledButtonCommand(buttons); + + // Ctrl+C and Ctrl+V shifts focus to some button because of which enter doesn't work after copy/paste. So don't shift focus if Ctrl+C or Ctrl+V + // is pressed. When drop down is open, pressing escape shifts focus to clear button. So dont's shift focus if drop down is open. Ctrl+Insert is + // equivalent to Ctrl+C and Shift+Insert is equivalent to Ctrl+V + auto currentIsDropDownOpen = s_IsDropDownOpen.find(viewId); + if (currentIsDropDownOpen == s_IsDropDownOpen.end() || !currentIsDropDownOpen->second) + { + // Do not Light Up Buttons when Ctrl+C, Ctrl+V, Ctrl+Insert or Shift+Insert is pressed + if (!(isControlKeyPressed && (key == VirtualKey::C || key == VirtualKey::V || key == VirtualKey::Insert)) + && !(isShiftKeyPressed && (key == VirtualKey::Insert))) + { + LightUpButtons(buttons); + } + } + } + } +} + +void KeyboardShortcutManager::OnAcceleratorKeyActivated(CoreDispatcher ^, AcceleratorKeyEventArgs ^ args) +{ + if (args->KeyStatus.IsKeyReleased) + { + auto key = args->VirtualKey; + const bool altPressed = args->KeyStatus.IsMenuKeyDown; + + // If the Alt/Menu key is not pressed then we don't care about the key anymore + if (!altPressed) + { + return; + } + + const bool controlKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + // Ctrl is pressed in addition to alt, this means Alt Gr is intended. do not navigate. + if (controlKeyPressed) + { + return; + } + + const bool shiftKeyPressed = (Window::Current->CoreWindow->GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down; + const auto lookupMap = GetCurrentKeyDictionary(controlKeyPressed, shiftKeyPressed, altPressed); + if (lookupMap != nullptr) + { + auto listItems = lookupMap->equal_range(static_cast(key)); + for (auto listIterator = listItems.first; listIterator != listItems.second; ++listIterator) + { + auto item = listIterator->second.Resolve(); + if (item != nullptr) + { + auto navView = safe_cast(item); + + auto menuItems = static_cast ^>(navView->MenuItemsSource); + if (menuItems != nullptr) + { + auto vm = safe_cast(navView->DataContext); + if (nullptr != vm) + { + ViewMode toMode = NavCategory::GetViewModeForVirtualKey(static_cast(key)); + auto nvi = dynamic_cast(menuItems->GetAt(NavCategory::GetFlatIndex(toMode))); + if (nvi && nvi->IsEnabled && NavCategory::IsValidViewMode(toMode)) + { + vm->Mode = toMode; + navView->SelectedItem = nvi; + } + } + } + break; + } + } + } + } +} + +void KeyboardShortcutManager::Initialize() +{ + auto coreWindow = Window::Current->CoreWindow; + coreWindow->CharacterReceived += + ref new TypedEventHandler(&KeyboardShortcutManager::OnCharacterReceivedHandler); + coreWindow->KeyDown += ref new TypedEventHandler(&KeyboardShortcutManager::OnKeyDownHandler); + coreWindow->Dispatcher->AcceleratorKeyActivated += + ref new TypedEventHandler(&KeyboardShortcutManager::OnAcceleratorKeyActivated); + + KeyboardShortcutManager::RegisterNewAppViewId(); +} + +void KeyboardShortcutManager::UpdateDropDownState(bool isOpen) +{ + int viewId = Utils::GetWindowId(); + + if (s_IsDropDownOpen.find(viewId) != s_IsDropDownOpen.end()) + { + s_IsDropDownOpen[viewId] = isOpen; + } +} + +void KeyboardShortcutManager::HonorShortcuts(bool allow) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + int viewId = Utils::GetWindowId(); + + if (s_fHonorShortcuts.find(viewId) != s_fHonorShortcuts.end()) + { + if (s_fDisableShortcuts.find(viewId) != s_fDisableShortcuts.end()) + { + if (s_fDisableShortcuts[viewId]) + { + s_fHonorShortcuts[viewId] = false; + return; + } + } + + s_fHonorShortcuts[viewId] = allow; + } +} + +void KeyboardShortcutManager::RegisterNewAppViewId() +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + int appViewId = Utils::GetWindowId(); + + // Check if the View Id has already been registered + if (s_characterForButtons.find(appViewId) == s_characterForButtons.end()) + { + s_characterForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_virtualKey.find(appViewId) == s_virtualKey.end()) + { + s_virtualKey.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyControlChordsForButtons.find(appViewId) == s_VirtualKeyControlChordsForButtons.end()) + { + s_VirtualKeyControlChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyShiftChordsForButtons.find(appViewId) == s_VirtualKeyShiftChordsForButtons.end()) + { + s_VirtualKeyShiftChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyAltChordsForButtons.find(appViewId) == s_VirtualKeyAltChordsForButtons.end()) + { + s_VirtualKeyAltChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + if (s_VirtualKeyControlShiftChordsForButtons.find(appViewId) == s_VirtualKeyControlShiftChordsForButtons.end()) + { + s_VirtualKeyControlShiftChordsForButtons.insert(make_pair(appViewId, multimap())); + } + + s_IsDropDownOpen[appViewId] = false; + s_ignoreNextEscape[appViewId] = false; + s_keepIgnoringEscape[appViewId] = false; + s_fHonorShortcuts[appViewId] = true; + s_fDisableShortcuts[appViewId] = false; +} + +void KeyboardShortcutManager::OnWindowClosed(int viewId) +{ + // Writer lock for the static maps + reader_writer_lock::scoped_lock lock(s_keyboardShortcutMapLock); + + s_characterForButtons.erase(viewId); + + s_virtualKey.erase(viewId); + s_VirtualKeyControlChordsForButtons.erase(viewId); + s_VirtualKeyShiftChordsForButtons.erase(viewId); + s_VirtualKeyAltChordsForButtons.erase(viewId); + s_VirtualKeyControlShiftChordsForButtons.erase(viewId); + + s_IsDropDownOpen.erase(viewId); + s_ignoreNextEscape.erase(viewId); + s_keepIgnoringEscape.erase(viewId); + s_fHonorShortcuts.erase(viewId); + s_fDisableShortcuts.erase(viewId); +} + +void KeyboardShortcutManager::DisableShortcuts(bool disable) +{ + int viewId = Utils::GetWindowId(); + + if (s_fDisableShortcuts.find(viewId) != s_fDisableShortcuts.end()) + { + s_fDisableShortcuts[viewId] = disable; + } + + HonorShortcuts(!disable); +} diff --git a/src/CalcViewModel/Common/KeyboardShortcutManager.h b/src/Calculator/Common/KeyboardShortcutManager.h similarity index 68% rename from src/CalcViewModel/Common/KeyboardShortcutManager.h rename to src/Calculator/Common/KeyboardShortcutManager.h index 6663f2ce..1ca7e487 100644 --- a/src/CalcViewModel/Common/KeyboardShortcutManager.h +++ b/src/Calculator/Common/KeyboardShortcutManager.h @@ -3,8 +3,8 @@ #pragma once -#include "Utils.h" -#include "MyVirtualKey.h" +#include "CalcViewModel/Common/Utils.h" +#include "CalcViewModel/Common/MyVirtualKey.h" namespace CalculatorApp { @@ -26,8 +26,6 @@ namespace CalculatorApp DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyShiftChord); DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyAltChord); DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlShiftChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyInverseChord); - DEPENDENCY_PROPERTY_ATTACHED_WITH_CALLBACK(MyVirtualKey, VirtualKeyControlInverseChord); internal : @@ -43,10 +41,8 @@ namespace CalculatorApp static void IgnoreEscape(bool onlyOnce); static void HonorEscape(); static void HonorShortcuts(bool allow); - static void HandledEnter(bool ishandled); + static void DisableShortcuts(bool disable); static void UpdateDropDownState(bool); - static void ShiftButtonChecked(bool checked); - static void UpdateDropDownState(Windows::UI::Xaml::Controls::Flyout ^ aboutPageFlyout); static void RegisterNewAppViewId(); static void OnWindowClosed(int viewId); @@ -60,11 +56,6 @@ namespace CalculatorApp static void OnVirtualKeyShiftChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); - static void OnVirtualKeyInverseChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); - - static void - OnVirtualKeyControlInverseChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); - static void OnVirtualKeyAltChordPropertyChanged(Windows::UI::Xaml::DependencyObject ^ target, MyVirtualKey oldValue, MyVirtualKey newValue); static void @@ -72,8 +63,25 @@ namespace CalculatorApp static void OnCharacterReceivedHandler(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::CharacterReceivedEventArgs ^ args); static void OnKeyDownHandler(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args); - static void OnKeyUpHandler(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::KeyEventArgs ^ args); static void OnAcceleratorKeyActivated(Windows::UI::Core::CoreDispatcher ^, Windows::UI::Core::AcceleratorKeyEventArgs ^ args); + static const std::multimap* + KeyboardShortcutManager::GetCurrentKeyDictionary(bool controlKeyPressed, bool shiftKeyPressed, bool altPressed); + + private: + static std::map> s_characterForButtons; + static std::map> s_virtualKey; + static std::map> s_VirtualKeyControlChordsForButtons; + static std::map> s_VirtualKeyShiftChordsForButtons; + static std::map> s_VirtualKeyAltChordsForButtons; + static std::map> s_VirtualKeyControlShiftChordsForButtons; + + static std::map s_IsDropDownOpen; + static std::map s_ignoreNextEscape; + static std::map s_keepIgnoringEscape; + static std::map s_fHonorShortcuts; + static std::map s_fDisableShortcuts; + + static Concurrency::reader_writer_lock s_keyboardShortcutMapLock; }; } } diff --git a/src/CalcViewModel/Common/ValidatingConverters.h b/src/Calculator/Common/ValidatingConverters.h similarity index 100% rename from src/CalcViewModel/Common/ValidatingConverters.h rename to src/Calculator/Common/ValidatingConverters.h diff --git a/src/Calculator/Common/ViewState.cpp b/src/Calculator/Common/ViewState.cpp new file mode 100644 index 00000000..63a5d30e --- /dev/null +++ b/src/Calculator/Common/ViewState.cpp @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "ViewState.h" + +namespace CalculatorApp +{ + namespace ViewState + { + Platform::StringReference Snap(L"Snap"); + Platform::StringReference DockedView(L"DockedView"); + + bool IsValidViewState(Platform::String ^ viewState) + { + return viewState->Equals(ViewState::Snap) || viewState->Equals(ViewState::DockedView); + } + } +} diff --git a/src/Calculator/Common/ViewState.h b/src/Calculator/Common/ViewState.h new file mode 100644 index 00000000..0a103d09 --- /dev/null +++ b/src/Calculator/Common/ViewState.h @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +namespace CalculatorApp +{ + namespace ViewState + { + extern Platform::StringReference Snap; + extern Platform::StringReference DockedView; + + bool IsValidViewState(Platform::String ^ viewState); + } +} diff --git a/src/Calculator/Controls/CalculationResult.cpp b/src/Calculator/Controls/CalculationResult.cpp index ed092fab..c2542f10 100644 --- a/src/Calculator/Controls/CalculationResult.cpp +++ b/src/Calculator/Controls/CalculationResult.cpp @@ -215,7 +215,7 @@ void CalculationResult::UpdateTextState() auto containerSize = m_textContainer->ActualWidth; String ^ oldText = m_textBlock->Text; - String ^ newText = Utils::LRO + DisplayValue + Utils::PDF; + String ^ newText = DisplayValue; // Initiate the scaling operation // UpdateLayout will keep calling us until we make it through the below 2 if-statements @@ -323,17 +323,38 @@ void CalculationResult::UpdateScrollButtons() return; } + bool shouldTryFocusScrollRight = false; if (m_scrollLeft != nullptr) { - m_scrollLeft->Visibility = m_textContainer->HorizontalOffset > SCROLL_BUTTONS_APPROXIMATION_RANGE ? ::Visibility::Visible : ::Visibility::Collapsed; + auto scrollLeftVisibility = m_textContainer->HorizontalOffset > SCROLL_BUTTONS_APPROXIMATION_RANGE ? ::Visibility::Visible : ::Visibility::Collapsed; + + if (scrollLeftVisibility == ::Visibility::Collapsed) + { + shouldTryFocusScrollRight = m_scrollLeft->Equals(FocusManager::GetFocusedElement()); + } + + m_scrollLeft->Visibility = scrollLeftVisibility; } if (m_scrollRight != nullptr) { - m_scrollRight->Visibility = + auto scrollRightVisibility = m_textContainer->HorizontalOffset + m_textContainer->ViewportWidth + SCROLL_BUTTONS_APPROXIMATION_RANGE < m_textContainer->ExtentWidth ? ::Visibility::Visible : ::Visibility::Collapsed; + + if (scrollRightVisibility == ::Visibility::Collapsed && m_scrollLeft != nullptr && m_scrollLeft->Visibility == ::Visibility::Visible + && m_scrollRight->Equals(FocusManager::GetFocusedElement())) + { + // ScrollRight had the focus and will be collapsed, ScrollLeft should get the focus + m_scrollLeft->Focus(::FocusState::Programmatic); + } + m_scrollRight->Visibility = scrollRightVisibility; + + if (shouldTryFocusScrollRight && scrollRightVisibility == ::Visibility::Visible) + { + m_scrollRight->Focus(::FocusState::Programmatic); + } } } diff --git a/src/Calculator/Controls/EquationTextBox.cpp b/src/Calculator/Controls/EquationTextBox.cpp new file mode 100644 index 00000000..a0d65298 --- /dev/null +++ b/src/Calculator/Controls/EquationTextBox.cpp @@ -0,0 +1,507 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "CalcViewModel/Common/AppResourceProvider.h" +#include "CalcViewModel/Common/LocalizationStringUtil.h" +#include "EquationTextBox.h" + +using namespace std; +using namespace Platform; +using namespace CalculatorApp; +using namespace CalculatorApp::Common; +using namespace CalculatorApp::Controls; +using namespace Windows::System; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel; +using namespace Windows::UI::Text; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Automation; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Media; + +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationColor); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonForegroundColor); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ColorChooserFlyout); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, EquationButtonContentIndex); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, HasError); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsAddEquationMode); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, MathEquation); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, ErrorText); +DEPENDENCY_PROPERTY_INITIALIZATION(EquationTextBox, IsEquationLineDisabled); + +EquationTextBox::EquationTextBox() +{ +} + +void EquationTextBox::OnApplyTemplate() +{ + m_equationButton = dynamic_cast(GetTemplateChild("EquationButton")); + m_richEditBox = dynamic_cast(GetTemplateChild("MathRichEditBox")); + m_deleteButton = dynamic_cast