Compare commits
80 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8c165c90a3 | ||
e911615845 | |||
|
d8ab8d5026 | ||
|
1eb7f13a05 | ||
|
74c5b403ae | ||
|
a64cc5a683 | ||
|
88589fd654 | ||
|
15219dedac | ||
|
d9da3a75e9 | ||
|
b8ba57a707 | ||
|
2c7670f06c | ||
|
5a5892edb7 | ||
41a0417065 | |||
bc9cc76fce | |||
|
dca25f6d89 | ||
|
88306f0091 | ||
|
72e0e47cd3 | ||
|
332c759d67 | ||
baaa585adc | |||
60461898ec | |||
|
44b8b36212 | ||
|
301347ad73 | ||
|
8480975309 | ||
c2bb03044b | |||
2faae1167b | |||
|
3985395141 | ||
|
781f7161e5 | ||
|
8a5a50fcaa | ||
|
ad8e178484 | ||
4a9f850c35 | |||
|
0ac4d1558f | ||
|
18c1f18210 | ||
d8e18e813d | |||
|
cb589569a1 | ||
|
5702fd6c98 | ||
|
258bf2a44d | ||
55e3c12e40 | |||
|
df55d0f1db | ||
|
2f803db1c8 | ||
b9de2cae2d | |||
d0e4de09cc | |||
|
9483bd01eb | ||
|
eaab9bb9d4 | ||
|
929e287af8 | ||
|
a8b0aa71a7 | ||
|
a314829e91 | ||
e1150d9e51 | |||
5d9a1e8fcf | |||
|
42d288f733 | ||
|
c361c2285c | ||
|
46004d85d5 | ||
6b0235fc3a | |||
6fe6db48a6 | |||
|
d17d586979 | ||
|
fb2bc5e0cd | ||
|
f262c10e4e | ||
|
7d6c26d7ef | ||
|
d523b4d51e | ||
|
7a1638ea40 | ||
|
de0486b0b0 | ||
|
fa6a24ea30 | ||
|
1667fc8b3d | ||
c624127bf9 | |||
|
7e40c1dc0b | ||
|
a212870d09 | ||
|
055c2fae03 | ||
|
0b06265117 | ||
f8c1ef0e96 | |||
|
e1b924c283 | ||
31a6fd090f | |||
0def6434d6 | |||
|
e45003ce6e | ||
|
0205e725ab | ||
|
7b1912579f | ||
22f9043e50 | |||
e176488f5c | |||
8110fe420e | |||
27e6c97712 |
|||
2fbffc2bcb |
|||
06206f5e98 |
71
AGENTS.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Modules
|
||||
- `src/AdvancedCalculator`: Shared UI + logic (Avalonia, ViewModels, Services, Models, Converters, Styles, Views, Assets).
|
||||
- `src/AdvancedCalculator.Desktop`: Desktop entry point (`net8.0`).
|
||||
- `src/AdvancedCalculator.Browser`: WebAssembly host (`net8.0-browser`, `wwwroot` for static assets).
|
||||
- `src/AdvancedCalculator.Android`: Android host (`net8.0-android`, `Resources`, `AndroidManifest.xml`).
|
||||
- `src/AdvancedCalculator.sln`: Solution root; `src/Directory.Build.props` centralizes common settings (nullable, Avalonia version).
|
||||
|
||||
## Build, Run, and Development
|
||||
- Restore + build all: `dotnet build src/AdvancedCalculator.sln -c Debug`
|
||||
- Run Desktop: `dotnet run --project src/AdvancedCalculator.Desktop -c Debug`
|
||||
- Run Browser: `dotnet run --project src/AdvancedCalculator.Browser -c Debug` (open the printed local URL)
|
||||
- Build Android: `dotnet build src/AdvancedCalculator.Android -c Debug` (install APK via `adb install` from `bin/Debug/net8.0-android/`)
|
||||
- Format code: `dotnet format` (run at repo root)
|
||||
|
||||
## Coding Style & Naming
|
||||
- C# 8+/latest, nullable enabled; 4‑space indentation, no tabs.
|
||||
- Names: PascalCase for types/properties; camelCase for locals/parameters; private fields `_camelCase`.
|
||||
- Use `var` for obvious types; prefer expression-bodied members when clear.
|
||||
- XAML: keep styles in `Styles/*.axaml`; avoid inline styling; prefer bindings and converters in `Converters/*`.
|
||||
|
||||
## Color Scheme (Official)
|
||||
### Primary Colors
|
||||
- Primary 1: `#5AC3D6` — Accent/brand; used for links, primary buttons, and focus highlights.
|
||||
- Primary 2: `#223544` — Deep surface; used for app chrome, panels, and elevated containers.
|
||||
- Primary 3: `#4E6D84` — Background shell; used for main window backgrounds and large surfaces.
|
||||
|
||||
### Emphasis Colors
|
||||
- Emphasis 1: `#F5A623` — Warning/attention highlights and subtle callouts.
|
||||
- Emphasis 2: `#D94E87` — Destructive/negative emphasis (e.g., remove/close actions).
|
||||
- Emphasis 3: `#49C46D` — Success/positive emphasis (e.g., confirmations, success badges).
|
||||
|
||||
### Background & Surfaces
|
||||
- Background: `#4E6D84` — Same as Primary 3; default app background.
|
||||
- Surface: `#223544` — Same as Primary 2; cards, drawers, panels.
|
||||
|
||||
### Text & Links
|
||||
- Text: `#EAF2F7` — Primary text on dark surfaces.
|
||||
- Link: `#5AC3D6` — Matches Primary 1.
|
||||
- Focus Ring: `#5AC3D6` — Matches Primary 1 for accessibility.
|
||||
|
||||
### Inputs & Borders
|
||||
- Muted Text: `#FFFFFFBF` — 75% white over surfaces (semi‑transparent tint).
|
||||
- Border: `#FFFFFF2E` — 18% white; subtle separators and dividers.
|
||||
- Input Background: `#FFFFFF0F` — 6% white; faint fill for inputs.
|
||||
- Input Border: `#FFFFFF38` — 22% white; input outlines on dark surfaces.
|
||||
|
||||
Note: 8‑digit hex values follow `#RRGGBBAA` (alpha last) and correspond to the previous RGBA tints.
|
||||
|
||||
### Avalonia Mapping
|
||||
- Define brushes in `Styles/Colors.axaml`: `CPrimary1`, `CPrimary2`, `CPrimary3`, `CEmph1`, `CEmph2`, `CEmph3`.
|
||||
- `ThemeAccentBrush`: derive from Primary 1.
|
||||
- Background/Surface brushes: use Background and Surface above.
|
||||
- Keep all UI colors derived from these tokens for consistency across Desktop, Browser, and Android.
|
||||
|
||||
## Testing Guidelines
|
||||
- No test projects yet. If adding tests, create `tests/AdvancedCalculator.Tests` (xUnit or NUnit), name files `*.Tests.cs`, and target `net8.0`.
|
||||
- Run tests with `dotnet test` at repo root or solution level.
|
||||
- Aim for coverage of `Services` and view‑model logic; UI/XAML can be validated via snapshot or behavior tests.
|
||||
|
||||
## Commit & Pull Requests
|
||||
- Messages: imperative mood, concise subject; optionally prefix type (e.g., `feat:`, `fix:`, `theme:`) as seen in history.
|
||||
- Include a brief body for non‑trivial changes with bullet points.
|
||||
- PRs: link related issues, describe scope and platform(s) affected (Desktop/Browser/Android), include screenshots/GIFs for UI changes, and note manual test steps.
|
||||
- Keep changes scoped; avoid unrelated refactors.
|
||||
|
||||
## Configuration Tips
|
||||
- Shared properties live in `src/Directory.Build.props`; prefer updating versions/nullable here, not per project.
|
||||
- Browser host static assets are under `src/AdvancedCalculator.Browser/wwwroot`.
|
||||
- Android package id is set in `AdvancedCalculator.Android.csproj` (`ApplicationId`). Update icons/splash in `Resources`.
|
68
README.md
|
@ -1,2 +1,66 @@
|
|||
# advanced-calculator
|
||||
A free-form calculator that uses CS-MIC to parse and process expressions.
|
||||
# Advanced Calculator
|
||||
|
||||
A simple, fast, free‑form calculator. You can type full math expressions, define variables, and see results right away. It runs on desktop, the web (WebAssembly), and Android. The math engine is powered by [CS‑MIC](https://github.com/wagesj45/cs-mic).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Easy input: Type expressions like `2 + 2 * 3` and press Enter.
|
||||
- Variables: Save values and reuse them in later expressions.
|
||||
- Linked expressions: Define variables that compute from other variables.
|
||||
- History: See past inputs and results; copy any item with a click.
|
||||
- Function browser: Browse built‑in functions and insert them quickly.
|
||||
- Cross‑platform: Desktop (Windows, macOS, Linux), Web, and Android.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Prerequisite: Install the .NET 8 SDK.
|
||||
|
||||
- Desktop: `dotnet run --project src/AdvancedCalculator.Desktop`
|
||||
- Web (WASM): `dotnet run --project src/AdvancedCalculator.Browser` then open the URL shown in the console.
|
||||
- Android: Build with `dotnet build src/AdvancedCalculator.Android -c Debug`. Install the APK from the `bin` folder using Android Studio or `adb`.
|
||||
|
||||
## How To Use
|
||||
|
||||
- Evaluate: Type an expression and press Enter, or click the Evaluate button.
|
||||
- Define variables:
|
||||
- Set a value: `x :: 5`
|
||||
- Link to an expression: `y := x * 2`
|
||||
The Variables panel shows both the stored value and, for linked ones, the current computed result.
|
||||
- Insert helpers: Use the Variables panel or the Functions button to insert names into the input box.
|
||||
- Copy: Right‑click a history item to copy the input, output, or both.
|
||||
|
||||
For all supported operators and functions, see the CS‑MIC usage guide: http://cs-mic.com/usage/
|
||||
|
||||
## Build and Publish
|
||||
|
||||
You can create self‑contained builds for desktop platforms.
|
||||
|
||||
- Windows (x64):
|
||||
- `dotnet publish src/AdvancedCalculator.Desktop -c Release -r win-x64 --self-contained true`
|
||||
- Linux (x64):
|
||||
- `dotnet publish src/AdvancedCalculator.Desktop -c Release -r linux-x64 --self-contained true`
|
||||
- macOS (Intel or Apple Silicon):
|
||||
- `dotnet publish src/AdvancedCalculator.Desktop -c Release -r osx-x64 --self-contained true`
|
||||
- `dotnet publish src/AdvancedCalculator.Desktop -c Release -r osx-arm64 --self-contained true`
|
||||
|
||||
Web build output is written to the `out` folder when running the Browser project.
|
||||
|
||||
## Technology
|
||||
|
||||
- UI: [Avalonia](https://avaloniaui.net/)
|
||||
- Language/runtime: C# on .NET 8
|
||||
- Math engine: [CS‑MIC](https://github.com/wagesj45/cs-mic)
|
||||
|
||||
## Privacy
|
||||
|
||||
This app does not send your data anywhere. All calculations run on your device.
|
||||
|
||||
## Contributing
|
||||
|
||||
Issues and pull requests are welcome. If you want to add features or fix bugs, please open an issue to discuss first.
|
||||
|
||||
## License
|
||||
|
||||
MIT License. See the LICENSE file for details.
|
||||
|
|
BIN
screen_shot.png
Normal file
After Width: | Height: | Size: 40 KiB |
454
src/.gitignore
vendored
Normal file
|
@ -0,0 +1,454 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
##
|
||||
## Visual Studio Code
|
||||
##
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
|
@ -0,0 +1,30 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0-android</TargetFramework>
|
||||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationId>com.jordanwages.advanced_calculator</ApplicationId>
|
||||
<!-- Version info is centralized in src/Directory.Build.props -->
|
||||
<AndroidPackageFormat>apk</AndroidPackageFormat>
|
||||
<AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Avalonia">
|
||||
<AvaloniaUseCompiledBindingsByDefault>False</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Icon.png">
|
||||
<Link>Resources\drawable\Icon.png</Link>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Android" Version="11.3.4" />
|
||||
<PackageReference Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.17" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AdvancedCalculator\AdvancedCalculator.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
BIN
src/AdvancedCalculator.Android/Icon.png
Normal file
After Width: | Height: | Size: 11 KiB |
23
src/AdvancedCalculator.Android/MainActivity.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.Views;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
|
||||
namespace AdvancedCalculator.Android;
|
||||
|
||||
[Activity(
|
||||
Label = "AdvancedCalculator.Android",
|
||||
Theme = "@style/MyTheme.NoActionBar",
|
||||
Icon = "@drawable/icon",
|
||||
MainLauncher = true,
|
||||
WindowSoftInputMode = SoftInput.AdjustResize,
|
||||
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
|
||||
public class MainActivity : AvaloniaMainActivity<App>
|
||||
{
|
||||
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
|
||||
{
|
||||
return base.CustomizeAppBuilder(builder)
|
||||
.WithInterFont();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="com.jordanwages.advanced_calculator">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:label="Advanced Calculator" android:icon="@drawable/icon" />
|
||||
</manifest>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item>
|
||||
<color android:color="@color/splash_background"/>
|
||||
</item>
|
||||
|
||||
<item android:drawable="@drawable/icon"
|
||||
android:width="120dp"
|
||||
android:height="120dp"
|
||||
android:gravity="center" />
|
||||
|
||||
</layer-list>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="splash_background">#212121</color>
|
||||
</resources>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="splash_background">#FFFFFF</color>
|
||||
</resources>
|
12
src/AdvancedCalculator.Android/Resources/values/styles.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
|
||||
<style name="MyTheme">
|
||||
</style>
|
||||
|
||||
<style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.DayNight.NoActionBar">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowBackground">@drawable/splash_screen</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
</resources>
|
18
src/AdvancedCalculator.Android/Styles/Android.axaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<!-- Android overrides: larger, touch-first -->
|
||||
<x:Double x:Key="FontSizeBase">16</x:Double>
|
||||
<x:Double x:Key="FontSizeLarge">18</x:Double>
|
||||
<x:Double x:Key="IconSizeM">24</x:Double>
|
||||
<x:Double x:Key="LineHeightBase">1.35</x:Double>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Thin overlay-like scrollbars -->
|
||||
<Style Selector="ScrollBar:vertical">
|
||||
<Setter Property="Width" Value="8"/>
|
||||
</Style>
|
||||
<Style Selector="ScrollBar:horizontal">
|
||||
<Setter Property="Height" Value="8"/>
|
||||
</Style>
|
||||
</Styles>
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.WebAssembly">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-browser</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Browser" Version="11.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AdvancedCalculator\AdvancedCalculator.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
17
src/AdvancedCalculator.Browser/Program.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using AdvancedCalculator;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Browser;
|
||||
|
||||
internal sealed partial class Program
|
||||
{
|
||||
private static Task Main(string[] args) => BuildAvaloniaApp()
|
||||
.WithInterFont()
|
||||
.StartBrowserAppAsync("out");
|
||||
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[assembly:System.Runtime.Versioning.SupportedOSPlatform("browser")]
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"profiles": {
|
||||
"AdvancedCalculator.Browser": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:7169;http://localhost:5235",
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
}
|
||||
}
|
||||
}
|
17
src/AdvancedCalculator.Browser/Styles/Browser.axaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<!-- Browser overrides: middle ground -->
|
||||
<x:Double x:Key="FontSizeBase">15</x:Double>
|
||||
<x:Double x:Key="IconSizeM">22</x:Double>
|
||||
<x:Double x:Key="LineHeightBase">1.3</x:Double>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Slightly thinner scrollbars on web -->
|
||||
<Style Selector="ScrollBar:vertical">
|
||||
<Setter Property="Width" Value="8"/>
|
||||
</Style>
|
||||
<Style Selector="ScrollBar:horizontal">
|
||||
<Setter Property="Height" Value="8"/>
|
||||
</Style>
|
||||
</Styles>
|
10
src/AdvancedCalculator.Browser/runtimeconfig.template.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"wasmHostProperties": {
|
||||
"perHostConfig": [
|
||||
{
|
||||
"name": "browser",
|
||||
"host": "browser"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
42
src/AdvancedCalculator.Browser/wwwroot/app.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* HTML styles for the splash screen */
|
||||
.avalonia-splash {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: white; /* light fallback */
|
||||
font-family: 'Outfit', sans-serif;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Light theme styles */
|
||||
@media (prefers-color-scheme: light) {
|
||||
.avalonia-splash { background: #FFFFFF; }
|
||||
.avalonia-splash h2 { color: #223544; } /* --c-primary-2 */
|
||||
.avalonia-splash a { color: #5AC3D6; } /* --c-primary-1 */
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.avalonia-splash { background: #223544; } /* --c-primary-2 */
|
||||
.avalonia-splash h2 { color: #FFFFFF; }
|
||||
.avalonia-splash a { color: #5AC3D6; } /* --c-primary-1 */
|
||||
}
|
||||
|
||||
.avalonia-splash h2 {
|
||||
font-weight: 400;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.avalonia-splash a {
|
||||
text-decoration: none;
|
||||
font-size: 2.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avalonia-splash.splash-close {
|
||||
transition: opacity 200ms, display 200ms;
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
37
src/AdvancedCalculator.Browser/wwwroot/index.html
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>AdvancedCalculator</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="./app.css" />
|
||||
<link rel="icon" href="./favicon.ico" sizes="any" />
|
||||
</head>
|
||||
|
||||
<body style="margin: 0; overflow: hidden">
|
||||
<div id="out">
|
||||
<div class="avalonia-splash">
|
||||
<h2>
|
||||
Powered by
|
||||
<a href="https://www.avaloniaui.net/" target="_blank">
|
||||
<svg width="266" height="52" viewBox="0 0 266 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M55.8592 47.3941C54.9035 47.3941 54.1184 47.1723 53.504 46.7285C52.9237 46.2848 52.5483 45.6875 52.3776 44.9365C52.2411 44.1856 52.3947 43.3493 52.8384 42.4277L65.9456 13.7045C66.4917 12.544 67.1403 11.7077 67.8912 11.1957C68.6421 10.6496 69.5125 10.3765 70.5024 10.3765C71.4923 10.3765 72.3627 10.6496 73.1136 11.1957C73.8645 11.7077 74.496 12.544 75.008 13.7045L88.2176 42.4277C88.6613 43.3493 88.8149 44.2027 88.6784 44.9877C88.576 45.7387 88.2347 46.336 87.6544 46.7797C87.0741 47.1893 86.3232 47.3941 85.4016 47.3941C84.2411 47.3941 83.3365 47.1211 82.688 46.5749C82.0736 46.0288 81.5275 45.1755 81.0496 44.0149L78.9279 39.0997H62.0415L59.9552 44.0149C59.4432 45.2096 58.8971 46.08 58.3168 46.6261C57.7707 47.1381 56.9515 47.3941 55.8592 47.3941ZM70.4 19.2853L64.6844 32.9045H76.2627L70.5024 19.2853H70.4Z" fill="currentColor"/>
|
||||
<path d="M101.869 47.3941C100.879 47.3941 100.009 47.1381 99.258 46.6261C98.5071 46.08 97.9096 45.2779 97.4659 44.2197L89.7348 26.4021C89.3593 25.5147 89.2228 24.6955 89.3252 23.9445C89.4276 23.1595 89.786 22.5451 90.4004 22.1013C91.0489 21.6235 91.9364 21.3845 93.0628 21.3845C93.9844 21.3845 94.7353 21.6064 95.3156 22.0501C95.8959 22.4597 96.4079 23.2619 96.8516 24.4565L102.018 37.95L107.552 24.4053C108.03 23.2448 108.559 22.4597 109.14 22.0501C109.72 21.6064 110.522 21.3845 111.546 21.3845C112.433 21.3845 113.133 21.6235 113.645 22.1013C114.191 22.5451 114.516 23.1424 114.618 23.8933C114.755 24.6443 114.618 25.4635 114.208 26.3509L106.324 44.2197C105.88 45.312 105.283 46.1141 104.532 46.6261C103.815 47.1381 102.927 47.3941 101.869 47.3941Z" fill="currentColor"/>
|
||||
<path d="M126.569 47.4965C124.726 47.4965 123.07 47.1381 121.602 46.4213C120.135 45.7045 118.991 44.7317 118.172 43.5029C117.353 42.2741 116.943 40.8917 116.943 39.3557C116.943 37.5125 117.421 36.0619 118.377 35.0037C119.333 33.9115 120.886 33.1435 123.036 32.6997C125.186 32.2219 128.037 31.9829 131.586 31.9829H133.43V35.9765H131.638C129.897 35.9765 128.48 36.0789 127.388 36.2837C126.33 36.4544 125.562 36.7616 125.084 37.2053C124.64 37.6491 124.418 38.2635 124.418 39.0485C124.418 40.0043 124.743 40.7893 125.391 41.4037C126.074 42.0181 127.047 42.3253 128.31 42.3253C129.299 42.3253 130.17 42.1035 130.921 41.6597C131.706 41.1819 132.32 40.5504 132.764 39.7653C133.208 38.9461 133.43 38.0245 133.43 37.0005V31.1125C133.43 29.6107 133.088 28.5525 132.406 27.9381C131.723 27.2896 130.562 26.9653 128.924 26.9653C128.002 26.9653 126.995 27.0848 125.903 27.3237C124.845 27.5285 123.667 27.8869 122.37 28.3989C121.619 28.7403 120.954 28.8256 120.374 28.6549C119.793 28.4501 119.35 28.1088 119.042 27.6309C118.735 27.1189 118.582 26.5728 118.582 25.9925C118.582 25.3781 118.752 24.7979 119.094 24.2517C119.435 23.6715 119.998 23.2448 120.783 22.9717C122.387 22.3232 123.889 21.8795 125.289 21.6405C126.722 21.4016 128.037 21.2821 129.231 21.2821C131.859 21.2821 134.01 21.6747 135.682 22.4597C137.389 23.2107 138.669 24.3883 139.522 25.9925C140.376 27.5627 140.802 29.5936 140.802 32.0853V43.4517C140.802 44.7147 140.495 45.6875 139.881 46.3701C139.266 47.0528 138.379 47.3941 137.218 47.3941C136.058 47.3941 135.153 47.0528 134.505 46.3701C133.89 45.6875 133.583 44.7147 133.583 43.4517L133.594 43.15C133.594 43.15 133.293 44.032 132.61 44.8853C131.962 45.7045 131.126 46.3531 130.102 46.8309C129.078 47.2747 127.9 47.4965 126.569 47.4965Z" fill="currentColor"/>
|
||||
<path d="M155.632 47.4965C152.594 47.4965 150.324 46.6603 148.822 44.9877C147.321 43.2811 146.57 40.7552 146.57 37.4101V14.3189C146.57 13.0219 146.894 12.0491 147.542 11.4005C148.225 10.7179 149.198 10.3765 150.461 10.3765C151.69 10.3765 152.628 10.7179 153.277 11.4005C153.959 12.0491 154.301 13.0219 154.301 14.3189V37.1029C154.301 38.5024 154.591 39.5435 155.171 40.2261C155.786 40.8747 156.588 41.1989 157.578 41.1989C157.851 41.1989 158.107 41.1819 158.346 41.1477C158.585 41.1136 158.841 41.0965 159.114 41.0965C159.66 41.0283 160.035 41.1989 160.24 41.6085C160.479 41.984 160.598 42.752 160.598 43.9125C160.598 44.9365 160.394 45.7216 159.984 46.2677C159.574 46.7797 158.943 47.1211 158.09 47.2917C157.748 47.3259 157.356 47.36 156.912 47.3941C156.468 47.4624 156.042 47.4965 155.632 47.4965Z" fill="currentColor"/>
|
||||
<path d="M175.453 47.4965C172.756 47.4965 170.401 46.9675 168.387 45.9093C166.407 44.8512 164.871 43.3323 163.779 41.3525C162.687 39.3728 162.141 37.0347 162.141 34.3381C162.141 32.3243 162.448 30.5152 163.062 28.9109C163.677 27.3067 164.564 25.9413 165.725 24.8149C166.919 23.6544 168.336 22.784 169.974 22.2037C171.613 21.5893 173.439 21.2821 175.453 21.2821C178.149 21.2821 180.487 21.8112 182.467 22.8693C184.481 23.9275 186.034 25.4293 187.126 27.3749C188.253 29.3205 188.816 31.6416 188.816 34.3381C188.816 36.3861 188.492 38.2123 187.843 39.8165C187.229 41.4208 186.341 42.8032 185.181 43.9637C184.02 45.1243 182.604 46.0117 180.931 46.6261C179.293 47.2064 177.467 47.4965 175.453 47.4965ZM175.453 41.7109C176.579 41.7109 177.552 41.4379 178.371 40.8917C179.19 40.3456 179.839 39.5435 180.317 38.4853C180.795 37.3931 181.034 36.0107 181.034 34.3381C181.034 31.8464 180.522 30.0203 179.498 28.8597C178.474 27.6651 177.125 27.0677 175.453 27.0677C174.361 27.0677 173.388 27.3237 172.534 27.8357C171.715 28.3477 171.067 29.1499 170.589 30.2421C170.145 31.3003 169.923 32.6656 169.923 34.3381C169.923 36.8299 170.435 38.6901 171.459 39.9189C172.483 41.1136 173.814 41.7109 175.453 41.7109Z" fill="currentColor"/>
|
||||
<path d="M197.411 47.3941C196.148 47.3941 195.175 47.0528 194.492 46.3701C193.844 45.6875 193.52 44.7147 193.52 43.4517V25.2757C193.52 24.0128 193.844 23.0571 194.492 22.4085C195.175 21.7259 196.114 21.3845 197.308 21.3845C198.537 21.3845 199.476 21.7259 200.124 22.4085C200.773 23.0571 201.112 24.1871 201.112 25.45C201.141 25.3955 202.48 23.552 204.016 22.6645C205.586 21.7429 207.361 21.2821 209.34 21.2821C211.354 21.2821 213.01 21.6747 214.307 22.4597C215.604 23.2107 216.577 24.3712 217.225 25.9413C217.874 27.4773 218.198 29.44 218.198 31.8293V43.4517C218.198 44.7147 217.857 45.6875 217.174 46.3701C216.525 47.0528 215.57 47.3941 214.307 47.3941C213.078 47.3941 212.122 47.0528 211.44 46.3701C210.791 45.6875 210.467 44.7147 210.467 43.4517V32.1877C210.467 30.4469 210.143 29.2011 209.494 28.4501C208.88 27.6651 207.924 27.2725 206.627 27.2725C204.988 27.2725 203.674 27.7845 202.684 28.8085C201.729 29.8325 201.251 31.1979 201.251 32.9045V43.4517C201.251 46.08 199.971 47.3941 197.411 47.3941Z" fill="currentColor"/>
|
||||
<path d="M227.861 47.3429C226.598 47.3429 225.625 46.9845 224.942 46.2677C224.294 45.5168 223.97 44.4757 223.97 43.1445V25.6341C223.97 24.2688 224.294 23.2277 224.942 22.5109C225.625 21.76 226.598 21.3845 227.861 21.3845C229.09 21.3845 230.028 21.76 230.677 22.5109C231.359 23.2277 231.701 24.2688 231.701 25.6341V43.1445C231.701 44.4757 231.377 45.5168 230.728 46.2677C230.079 46.9845 229.124 47.3429 227.861 47.3429ZM227.861 17.1861C226.427 17.1861 225.318 16.8619 224.533 16.2133C223.782 15.5307 223.406 14.5749 223.406 13.3461C223.406 12.0832 223.782 11.1275 224.533 10.4789C225.318 9.79629 226.427 9.45496 227.861 9.45496C229.294 9.45496 230.387 9.79629 231.138 10.4789C231.889 11.1275 232.264 12.0832 232.264 13.3461C232.264 14.5749 231.889 15.5307 231.138 16.2133C230.387 16.8619 229.294 17.1861 227.861 17.1861Z" fill="currentColor"/>
|
||||
<path d="M246.169 47.4965C244.326 47.4965 242.67 47.1381 241.202 46.4213C239.735 45.7045 238.591 44.7317 237.772 43.5029C236.953 42.2741 236.543 40.8917 236.543 39.3557C236.543 37.5125 237.021 36.0619 237.977 35.0037C238.933 33.9115 240.486 33.1435 242.636 32.6997C244.786 32.2219 247.637 31.9829 251.186 31.9829H253.03V35.9765H251.238C249.497 35.9765 248.08 36.0789 246.988 36.2837C245.93 36.4544 245.162 36.7616 244.684 37.2053C244.24 37.6491 244.018 38.2635 244.018 39.0485C244.018 40.0043 244.343 40.7893 244.991 41.4037C245.674 42.0181 246.647 42.3253 247.91 42.3253C248.899 42.3253 249.77 42.1035 250.521 41.6597C251.306 41.1819 251.92 40.5504 252.364 39.7653C252.808 38.9461 253.03 38.0245 253.03 37.0005V31.1125C253.03 29.6107 252.688 28.5525 252.006 27.9381C251.323 27.2896 250.162 26.9653 248.524 26.9653C247.602 26.9653 246.595 27.0848 245.503 27.3237C244.445 27.5285 243.267 27.8869 241.97 28.3989C241.219 28.7403 240.554 28.8256 239.974 28.6549C239.393 28.4501 238.95 28.1088 238.642 27.6309C238.335 27.1189 238.182 26.5728 238.182 25.9925C238.182 25.3781 238.352 24.7979 238.694 24.2517C239.035 23.6715 239.598 23.2448 240.383 22.9717C241.987 22.3232 243.489 21.8795 244.889 21.6405C246.322 21.4016 247.637 21.2821 248.831 21.2821C251.459 21.2821 253.61 21.6747 255.282 22.4597C256.989 23.2107 258.269 24.3883 259.122 25.9925C259.976 27.5627 260.402 29.5936 260.402 32.0853V43.4517C260.402 44.7147 260.095 45.6875 259.481 46.3701C258.866 47.0528 257.979 47.3941 256.818 47.3941C255.658 47.3941 254.753 47.0528 254.105 46.3701C253.49 45.6875 253.183 44.7147 253.183 43.4517V43.1789C253.183 43.3144 252.893 44.032 252.21 44.8853C251.562 45.7045 250.726 46.3531 249.702 46.8309C248.678 47.2747 247.5 47.4965 246.169 47.4965Z" fill="currentColor"/>
|
||||
<path d="M22.3444 20.9916C18.7895 20.9916 15.9077 24.0073 15.9077 27.7274C15.9077 31.4475 18.7895 34.4632 22.3444 34.4632C25.8993 34.4632 28.7811 31.4475 28.7811 27.7274C28.7811 24.0073 25.8993 20.9916 22.3444 20.9916Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.6937 49.0667H21.9271C10.8346 48.8653 1.90173 39.3893 1.90173 27.7333C1.90173 15.9513 11.0289 6.40002 22.2878 6.40002C33.3638 6.40002 42.3768 15.6435 42.6667 27.161L42.6314 44.4824C42.338 47.0679 40.2434 49.0667 37.6937 49.0667ZM9.22617 24.667C10.5612 18.3725 15.9275 13.6656 22.3444 13.6656C29.7657 13.6656 35.7818 19.9613 35.7818 27.7274C35.7818 27.7857 35.7825 27.8488 35.7831 27.9136C35.7846 28.0483 35.7861 28.1901 35.7818 28.3103V41.6939H28.7907V40.0685C26.877 41.1655 24.6803 41.7892 22.3444 41.7892C15.9275 41.7892 10.5612 37.0823 9.22617 30.7878C10.5043 30.4129 11.4416 29.1847 11.4416 27.7274C11.4416 26.2701 10.5043 25.0419 9.22617 24.667ZM8.33937 29.9683C9.52696 29.9683 10.4897 28.9609 10.4897 27.7181C10.4897 26.4753 9.52696 25.4678 8.33937 25.4678C7.15178 25.4678 6.18904 26.4753 6.18904 27.7181C6.18904 28.9609 7.15178 29.9683 8.33937 29.9683Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</a>
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<script type='module' src="./main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
13
src/AdvancedCalculator.Browser/wwwroot/main.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { dotnet } from './_framework/dotnet.js'
|
||||
|
||||
const is_browser = typeof window != "undefined";
|
||||
if (!is_browser) throw new Error(`Expected to be running in a browser`);
|
||||
|
||||
const dotnetRuntime = await dotnet
|
||||
.withDiagnosticTracing(false)
|
||||
.withApplicationArgumentsFromQuery()
|
||||
.create();
|
||||
|
||||
const config = dotnetRuntime.getConfig();
|
||||
|
||||
await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]);
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<!--If you are willing to use Windows/MacOS native APIs you will need to create 3 projects.
|
||||
One for Windows with net8.0-windows TFM, one for MacOS with net8.0-macos and one with net8.0 TFM for Linux.-->
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>app.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AdvancedCalculator\AdvancedCalculator.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
23
src/AdvancedCalculator.Desktop/Program.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
|
||||
using Avalonia;
|
||||
|
||||
namespace AdvancedCalculator.Desktop;
|
||||
|
||||
class Program
|
||||
{
|
||||
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||
// yet and stuff might break.
|
||||
[STAThread]
|
||||
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace();
|
||||
|
||||
}
|
17
src/AdvancedCalculator.Desktop/Styles/Desktop.axaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<!-- Desktop overrides: slightly denser -->
|
||||
<x:Double x:Key="FontSizeBase">13</x:Double>
|
||||
<x:Double x:Key="LineHeightBase">1.2</x:Double>
|
||||
<x:Double x:Key="IconSizeM">20</x:Double>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Scrollbars a bit thicker on desktop -->
|
||||
<Style Selector="ScrollBar:vertical">
|
||||
<Setter Property="Width" Value="12"/>
|
||||
</Style>
|
||||
<Style Selector="ScrollBar:horizontal">
|
||||
<Setter Property="Height" Value="12"/>
|
||||
</Style>
|
||||
</Styles>
|
BIN
src/AdvancedCalculator.Desktop/app.ico
Normal file
After Width: | Height: | Size: 90 KiB |
18
src/AdvancedCalculator.Desktop/app.manifest
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<!-- This manifest is used on Windows only.
|
||||
Don't remove it as it might cause problems with window transparency and embeded controls.
|
||||
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||
<assemblyIdentity version="1.0.0.0" name="AvaloniaTest.Desktop"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
|
@ -1,118 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>AdvancedCalculator</RootNamespace>
|
||||
<AssemblyName>AdvancedCalculator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="csmic, Version=1.1.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\csmic.1.1.4\lib\net40\csmic.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.AvalonEdit, Version=5.0.3.0, Culture=neutral, PublicKeyToken=9cc39be672370310, processorArchitecture=MSIL">
|
||||
<HintPath>packages\AvalonEdit.5.0.4\lib\Net40\ICSharpCode.AvalonEdit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="FunctionDefinitonItem.cs" />
|
||||
<Compile Include="IconFont.cs" />
|
||||
<Compile Include="VariableItem.cs" />
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="HistoryItem.cs" />
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<Resource Include="materialdesignicons-webfont.ttf" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="calculator-variant-black.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,9 +1,21 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2026
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35707.178
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedCalculator", "AdvancedCalculator.csproj", "{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedCalculator", "AdvancedCalculator\AdvancedCalculator.csproj", "{690788A5-B548-4BE1-8B01-966BBE907C2D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedCalculator.Android", "AdvancedCalculator.Android\AdvancedCalculator.Android.csproj", "{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedCalculator.Browser", "AdvancedCalculator.Browser\AdvancedCalculator.Browser.csproj", "{8FF50DD6-23E0-4CF2-A98D-0FBC649DB2DD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedCalculator.Desktop", "AdvancedCalculator.Desktop\AdvancedCalculator.Desktop.csproj", "{E39C7683-E7DA-4730-B805-7F1F9DB624CB}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
build.ps1 = build.ps1
|
||||
Directory.Build.props = Directory.Build.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -11,15 +23,29 @@ Global
|
|||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{05F9F945-8B05-4FDC-82E8-74538C0E6B0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{690788A5-B548-4BE1-8B01-966BBE907C2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{690788A5-B548-4BE1-8B01-966BBE907C2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{690788A5-B548-4BE1-8B01-966BBE907C2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{690788A5-B548-4BE1-8B01-966BBE907C2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{816B4AC8-25A3-483C-9AC2-8DA1EB8687AC}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{8FF50DD6-23E0-4CF2-A98D-0FBC649DB2DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8FF50DD6-23E0-4CF2-A98D-0FBC649DB2DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8FF50DD6-23E0-4CF2-A98D-0FBC649DB2DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8FF50DD6-23E0-4CF2-A98D-0FBC649DB2DD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E39C7683-E7DA-4730-B805-7F1F9DB624CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E39C7683-E7DA-4730-B805-7F1F9DB624CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E39C7683-E7DA-4730-B805-7F1F9DB624CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E39C7683-E7DA-4730-B805-7F1F9DB624CB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {68575B5E-E0A7-4DC5-B2C9-66A991BCDCBE}
|
||||
SolutionGuid = {223763A0-49A6-44DD-8702-1FB65776F5E9}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
24
src/AdvancedCalculator/AdvancedCalculator.csproj
Normal file
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.3.4" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.4" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.4" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)" />
|
||||
<PackageReference Include="CSMic.StandardLibrary" Version="2.0.0-beta-07" />
|
||||
</ItemGroup>
|
||||
</Project>
|
19
src/AdvancedCalculator/App.axaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="AdvancedCalculator.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<StyleInclude Source="avares://AdvancedCalculator/Styles/Colors.axaml"/>
|
||||
<StyleInclude Source="avares://AdvancedCalculator/Styles/Buttons.axaml"/>
|
||||
<StyleInclude Source="avares://AdvancedCalculator/Styles/Typography.axaml"/>
|
||||
<StyleInclude Source="avares://AdvancedCalculator/Styles/Scroll.axaml"/>
|
||||
</Application.Styles>
|
||||
|
||||
<Application.Resources>
|
||||
<!-- Material Design Icons font family embedded as an Avalonia resource -->
|
||||
<FontFamily x:Key="MDI">avares://AdvancedCalculator/Assets/Fonts#Material Design Icons</FontFamily>
|
||||
</Application.Resources>
|
||||
</Application>
|
78
src/AdvancedCalculator/App.axaml.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using AdvancedCalculator.ViewModels;
|
||||
using AdvancedCalculator.Views;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Markup.Xaml.Styling;
|
||||
using Avalonia.Styling;
|
||||
using System;
|
||||
|
||||
namespace AdvancedCalculator;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
// Line below is needed to remove Avalonia data validation.
|
||||
// Without this line you will get duplicate validations from both Avalonia and CT
|
||||
BindingPlugins.DataValidators.RemoveAt(0);
|
||||
|
||||
// Load platform-specific styles (typography, scrollbars overrides)
|
||||
try
|
||||
{
|
||||
var styles = Current?.Styles;
|
||||
if (styles is not null)
|
||||
{
|
||||
if (OperatingSystem.IsAndroid())
|
||||
{
|
||||
styles.Add(new StyleInclude(new Uri("avares://AdvancedCalculator/"))
|
||||
{
|
||||
Source = new Uri("avares://AdvancedCalculator.Android/Styles/Android.axaml")
|
||||
});
|
||||
}
|
||||
else if (OperatingSystem.IsBrowser())
|
||||
{
|
||||
styles.Add(new StyleInclude(new Uri("avares://AdvancedCalculator/"))
|
||||
{
|
||||
Source = new Uri("avares://AdvancedCalculator.Browser/Styles/Browser.axaml")
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
styles.Add(new StyleInclude(new Uri("avares://AdvancedCalculator/"))
|
||||
{
|
||||
Source = new Uri("avares://AdvancedCalculator.Desktop/Styles/Desktop.axaml")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If a platform-specific style dictionary is missing, continue without failing.
|
||||
}
|
||||
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow
|
||||
{
|
||||
DataContext = new MainViewModel()
|
||||
};
|
||||
}
|
||||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
|
||||
{
|
||||
singleViewPlatform.MainView = new MainView
|
||||
{
|
||||
DataContext = new MainViewModel()
|
||||
};
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
BIN
src/AdvancedCalculator/Assets/avalonia-logo.ico
Normal file
After Width: | Height: | Size: 90 KiB |
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
public class BoolToGridLengthConverter : IValueConverter
|
||||
{
|
||||
public static readonly BoolToGridLengthConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo? culture)
|
||||
{
|
||||
var isOpen = value is bool b && b;
|
||||
// Return star when true, otherwise zero pixels
|
||||
return isOpen ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, System.Globalization.CultureInfo? culture)
|
||||
{
|
||||
if (value is GridLength gl)
|
||||
{
|
||||
return gl.Value > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
using AdvancedCalculator.Models;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
public class CategoryToBrushConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is not FunctionCategory cat) return Brushes.Gray;
|
||||
|
||||
// Soft header backgrounds derived from brand palette (with transparency)
|
||||
// Brand colors:
|
||||
// Primary1: #5AC3D6, Primary2: #223544, Primary3: #4E6D84
|
||||
// Emph1: #F5A623, Emph2: #D94E87, Emph3: #49C46D
|
||||
return cat switch
|
||||
{
|
||||
FunctionCategory.Base => new SolidColorBrush(Color.FromArgb(0x26, 0x5A, 0xC3, 0xD6)), // Primary1 @ ~15%
|
||||
FunctionCategory.Angle => new SolidColorBrush(Color.FromArgb(0x26, 0x49, 0xC4, 0x6D)), // Emph3 @ ~15%
|
||||
FunctionCategory.Rounding => new SolidColorBrush(Color.FromArgb(0x26, 0xF5, 0xA6, 0x23)), // Emph1 @ ~15%
|
||||
FunctionCategory.Trigonometry => new SolidColorBrush(Color.FromArgb(0x26, 0x4E, 0x6D, 0x84)), // Primary3 @ ~15%
|
||||
FunctionCategory.HyperbolicTrig => new SolidColorBrush(Color.FromArgb(0x1A, 0x5A, 0xC3, 0xD6)), // Primary1 @ ~10%
|
||||
FunctionCategory.NumberTheory => new SolidColorBrush(Color.FromArgb(0x1A, 0xF5, 0xA6, 0x23)), // Emph1 @ ~10%
|
||||
FunctionCategory.Random => new SolidColorBrush(Color.FromArgb(0x26, 0xD9, 0x4E, 0x87)), // Emph2 @ ~15%
|
||||
_ => Brushes.Gray
|
||||
};
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotSupportedException();
|
||||
}
|
25
src/AdvancedCalculator/Converters/IsZeroConverter.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
public class IsZeroConverter : IValueConverter
|
||||
{
|
||||
public static readonly IsZeroConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
{
|
||||
if (value is int i)
|
||||
return i == 0;
|
||||
if (value is long l)
|
||||
return l == 0L;
|
||||
if (value is double d)
|
||||
return Math.Abs(d) < double.Epsilon;
|
||||
return false;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
31
src/AdvancedCalculator/Converters/WidthToBooleanConverter.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
// Returns true if width (double) is less than the provided threshold (parameter),
|
||||
// otherwise false. Default threshold is 640 when parameter is null or invalid.
|
||||
public class WidthToBooleanConverter : IValueConverter
|
||||
{
|
||||
public static readonly WidthToBooleanConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
{
|
||||
if (value is double width)
|
||||
{
|
||||
double threshold = 640;
|
||||
if (parameter is double p)
|
||||
threshold = p;
|
||||
else if (parameter is string s && double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var parsed))
|
||||
threshold = parsed;
|
||||
|
||||
return width < threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
public class WidthToPaneOpenConverter : IValueConverter
|
||||
{
|
||||
public static readonly WidthToPaneOpenConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
{
|
||||
if (value is double width)
|
||||
{
|
||||
double threshold = 640;
|
||||
if (parameter is double p)
|
||||
threshold = p;
|
||||
else if (parameter is string s && double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var parsed))
|
||||
threshold = parsed;
|
||||
|
||||
return width >= threshold;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace AdvancedCalculator.Converters;
|
||||
|
||||
public class WidthToSplitViewModeConverter : IValueConverter
|
||||
{
|
||||
public static readonly WidthToSplitViewModeConverter Instance = new();
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
{
|
||||
if (value is double width)
|
||||
{
|
||||
double threshold = 640;
|
||||
if (parameter is double p)
|
||||
threshold = p;
|
||||
else if (parameter is string s && double.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var parsed))
|
||||
threshold = parsed;
|
||||
|
||||
return width < threshold ? SplitViewDisplayMode.Overlay : SplitViewDisplayMode.Inline;
|
||||
}
|
||||
return SplitViewDisplayMode.Inline;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo? culture)
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
|
121
src/AdvancedCalculator/Models/FunctionDefinitionItem.cs
Normal file
|
@ -0,0 +1,121 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AdvancedCalculator.Models;
|
||||
|
||||
public class FunctionDefinitionItem
|
||||
{
|
||||
public string FunctionName { get; private set; } = string.Empty;
|
||||
public string FunctionDescription { get; private set; } = string.Empty;
|
||||
public IEnumerable<KeyValuePair<string, string>> FunctionArguments { get; private set; } = new List<KeyValuePair<string, string>>();
|
||||
public FunctionCategory Category { get; private set; }
|
||||
|
||||
public string Signature => $"{FunctionName}({string.Join(", ", FunctionArguments.Select(a => a.Key))})";
|
||||
|
||||
public static IEnumerable<FunctionDefinitionItem> DefinedFunctions
|
||||
{
|
||||
get
|
||||
{
|
||||
// Base
|
||||
yield return new FunctionDefinitionItem { FunctionName = "abs", Category = FunctionCategory.Base, FunctionDescription = "Absolute value of a number.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "sign", Category = FunctionCategory.Base, FunctionDescription = "Sign of a number: 1 or -1.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "min", Category = FunctionCategory.Base, FunctionDescription = "Minimum of two numbers.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "max", Category = FunctionCategory.Base, FunctionDescription = "Maximum of two numbers.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
|
||||
// Angle
|
||||
yield return new FunctionDefinitionItem { FunctionName = "degrees", Category = FunctionCategory.Angle, FunctionDescription = "Converts radians to degrees.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "radians", Category = FunctionCategory.Angle, FunctionDescription = "Converts degrees to radians.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "wrapangle", Category = FunctionCategory.Angle, FunctionDescription = "Wraps angle into [start, end).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number"), new KeyValuePair<string, string>("periodStart", "number"), new KeyValuePair<string, string>("periodEnd", "number") } };
|
||||
|
||||
// Rounding
|
||||
yield return new FunctionDefinitionItem { FunctionName = "floor", Category = FunctionCategory.Rounding, FunctionDescription = "Largest integer ≤ value.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "ceiling", Category = FunctionCategory.Rounding, FunctionDescription = "Smallest integer ≥ value.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "truncate", Category = FunctionCategory.Rounding, FunctionDescription = "Integer part toward zero.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "round", Category = FunctionCategory.Rounding, FunctionDescription = "Round to precision (banker's).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number"), new KeyValuePair<string, string>("precision", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "frac", Category = FunctionCategory.Rounding, FunctionDescription = "Fractional part of a number.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "clamp", Category = FunctionCategory.Rounding, FunctionDescription = "Clamp to [low, high].", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number"), new KeyValuePair<string, string>("low", "number"), new KeyValuePair<string, string>("high", "number") } };
|
||||
|
||||
// Trigonometry
|
||||
yield return new FunctionDefinitionItem { FunctionName = "sin", Category = FunctionCategory.Trigonometry, FunctionDescription = "Sine of angle (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "cos", Category = FunctionCategory.Trigonometry, FunctionDescription = "Cosine of angle (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "tan", Category = FunctionCategory.Trigonometry, FunctionDescription = "Tangent of angle (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "asin", Category = FunctionCategory.Trigonometry, FunctionDescription = "Arcsine (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "acos", Category = FunctionCategory.Trigonometry, FunctionDescription = "Arccosine (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "atan", Category = FunctionCategory.Trigonometry, FunctionDescription = "Arctangent (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "atan2", Category = FunctionCategory.Trigonometry, FunctionDescription = "Arctan(y/x) (radians).", FunctionArguments = new[] { new KeyValuePair<string, string>("y", "number"), new KeyValuePair<string, string>("x", "number") } };
|
||||
|
||||
// Hyperbolic Trig
|
||||
yield return new FunctionDefinitionItem { FunctionName = "sinh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Hyperbolic sine.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "cosh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Hyperbolic cosine.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "tanh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Hyperbolic tangent.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "asinh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Inverse hyperbolic sine.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "acosh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Inverse hyperbolic cosine.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "atanh", Category = FunctionCategory.HyperbolicTrig, FunctionDescription = "Inverse hyperbolic tangent.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
|
||||
// Number Theory
|
||||
yield return new FunctionDefinitionItem { FunctionName = "fac", Category = FunctionCategory.NumberTheory, FunctionDescription = "Factorial; Γ(n+1) for non-integers; 0 outside 0..20.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "ncr", Category = FunctionCategory.NumberTheory, FunctionDescription = "Binomial coefficient 0 ≤ r ≤ n ≤ 20.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "npr", Category = FunctionCategory.NumberTheory, FunctionDescription = "Permutations 0 ≤ r ≤ n ≤ 20.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "gcd", Category = FunctionCategory.NumberTheory, FunctionDescription = "GCD of two positive integers.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "lcm", Category = FunctionCategory.NumberTheory, FunctionDescription = "LCM of two positive integers.", FunctionArguments = new[] { new KeyValuePair<string, string>("first", "number"), new KeyValuePair<string, string>("second", "number") } };
|
||||
|
||||
// Random
|
||||
yield return new FunctionDefinitionItem { FunctionName = "flip", Category = FunctionCategory.Random, FunctionDescription = "Fair coin flip → boolean.", FunctionArguments = Enumerable.Empty<KeyValuePair<string, string>>() };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "bern", Category = FunctionCategory.Random, FunctionDescription = "Bernoulli trial with probability p.", FunctionArguments = new[] { new KeyValuePair<string, string>("p", "number in [0,1]") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "rand", Category = FunctionCategory.Random, FunctionDescription = "Uniform random in [0,1).", FunctionArguments = Enumerable.Empty<KeyValuePair<string, string>>() };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "rands", Category = FunctionCategory.Random, FunctionDescription = "Uniform random in [lower, upper).", FunctionArguments = new[] { new KeyValuePair<string, string>("lower", "number"), new KeyValuePair<string, string>("upper", "number") } };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "randn", Category = FunctionCategory.Random, FunctionDescription = "Standard normal N(0,1).", FunctionArguments = Enumerable.Empty<KeyValuePair<string, string>>() };
|
||||
yield return new FunctionDefinitionItem { FunctionName = "randns", Category = FunctionCategory.Random, FunctionDescription = "Normal scaled by (upper − lower).", FunctionArguments = new[] { new KeyValuePair<string, string>("lower", "number"), new KeyValuePair<string, string>("upper", "number") } };
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<FunctionGroup> DefinedFunctionGroups
|
||||
{
|
||||
get
|
||||
{
|
||||
var groups = new[]
|
||||
{
|
||||
new { Category = FunctionCategory.Base, Name = "Base", Icon = IconFont.FunctionVariant },
|
||||
new { Category = FunctionCategory.Angle, Name = "Angle", Icon = IconFont.Protractor },
|
||||
new { Category = FunctionCategory.Rounding, Name = "Rounding", Icon = IconFont.Ruler },
|
||||
new { Category = FunctionCategory.Trigonometry, Name = "Trigonometry", Icon = IconFont.SineWave },
|
||||
new { Category = FunctionCategory.HyperbolicTrig, Name = "Hyperbolic Trig", Icon = IconFont.Waveform },
|
||||
new { Category = FunctionCategory.NumberTheory, Name = "Number Theory", Icon = IconFont.Abacus },
|
||||
new { Category = FunctionCategory.Random, Name = "Random", Icon = IconFont.DiceMultipleOutline },
|
||||
};
|
||||
|
||||
var all = DefinedFunctions.ToList();
|
||||
foreach (var g in groups)
|
||||
{
|
||||
var items = all.Where(f => f.Category == g.Category).OrderBy(f => f.FunctionName).ToList();
|
||||
if (items.Count == 0) continue;
|
||||
yield return new FunctionGroup
|
||||
{
|
||||
Category = g.Category,
|
||||
GroupName = g.Name,
|
||||
Icon = g.Icon,
|
||||
Functions = items
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FunctionCategory
|
||||
{
|
||||
Base,
|
||||
Angle,
|
||||
Rounding,
|
||||
Trigonometry,
|
||||
HyperbolicTrig,
|
||||
NumberTheory,
|
||||
Random
|
||||
}
|
||||
|
||||
public class FunctionGroup
|
||||
{
|
||||
public FunctionCategory Category { get; set; }
|
||||
public string GroupName { get; set; } = string.Empty;
|
||||
public string Icon { get; set; } = string.Empty; // MDI glyph
|
||||
public IEnumerable<FunctionDefinitionItem> Functions { get; set; } = new List<FunctionDefinitionItem>();
|
||||
}
|
8
src/AdvancedCalculator/Models/HistoryItem.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace AdvancedCalculator.Models;
|
||||
|
||||
public class HistoryItem
|
||||
{
|
||||
public string Input { get; set; } = string.Empty;
|
||||
public string Output { get; set; } = string.Empty;
|
||||
}
|
||||
|
25
src/AdvancedCalculator/Models/IconFont.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
namespace AdvancedCalculator.Models;
|
||||
|
||||
// Slimmed icon font mapping for required glyphs only
|
||||
public static class IconFont
|
||||
{
|
||||
public const string ArrowRightDropCircle = "\U000f0059";
|
||||
public const string DecimalIncrease = "\U000f01b5";
|
||||
public const string Function = "\U000f0295";
|
||||
public const string FunctionVariant = "\U000f070f";
|
||||
public const string PlusCircle = "\U000f0417";
|
||||
public const string RoundedCorner = "\U000f0607";
|
||||
public const string SquareRoot = "\U000f0784";
|
||||
public const string SineWave = "\U000f095b";
|
||||
public const string Variable = "\U000f0ae7";
|
||||
public const string AlphaECircle = "\U000f0bf8";
|
||||
public const string MathTan = "\U000f0c98";
|
||||
public const string MathLog = "\U000f1085";
|
||||
public const string CosineWave = "\U000f1479";
|
||||
public const string AngleAcute = "\U000f0937";
|
||||
public const string Protractor = "\U000f0b4f";
|
||||
public const string Ruler = "\U000f0463";
|
||||
public const string Waveform = "\U000f147c";
|
||||
public const string Abacus = "\U000f16e0";
|
||||
public const string DiceMultipleOutline = "\U000f1156";
|
||||
}
|
15
src/AdvancedCalculator/Models/VariableItem.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace AdvancedCalculator.Models;
|
||||
|
||||
public class VariableItem
|
||||
{
|
||||
public string VariableName { get; set; } = string.Empty;
|
||||
public string Icon { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
|
||||
// True when this variable is defined by an equation/expression
|
||||
public bool IsExpression { get; set; }
|
||||
|
||||
// Computed value for an expression variable (pretty-printed)
|
||||
public string? ExpressionComputation { get; set; }
|
||||
}
|
||||
|
69
src/AdvancedCalculator/Services/CalculatorService.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedCalculator.Models;
|
||||
|
||||
// Intentionally keep the csmic/CSMic dependency surface in this file only.
|
||||
// This allows easy adaptation if the interpreter API changes.
|
||||
namespace AdvancedCalculator.Services;
|
||||
|
||||
public class CalculatorService : ICalculatorService
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
|
||||
private readonly CSMic.InputInterpreter _interpreter = new();
|
||||
|
||||
public CalculatorService()
|
||||
{
|
||||
CSMic.StandardLibrary.Initializer.InitializeAllFunctions(_interpreter);
|
||||
}
|
||||
|
||||
public Task<InterpretResult> InterpretAsync(string input)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_interpreter.Interpret(input);
|
||||
var output = string.IsNullOrWhiteSpace(_interpreter.StringValue) ? _interpreter.NumericValue.ToString() : _interpreter.StringValue;
|
||||
|
||||
// Build variables list
|
||||
var variables = new List<VariableItem>();
|
||||
foreach (var csmicVariable in _interpreter.Variables)
|
||||
{
|
||||
var name = csmicVariable.Name;
|
||||
var variable = csmicVariable.Value;
|
||||
var valueString = variable?.ToString() ?? string.Empty;
|
||||
|
||||
var isExpression = csmicVariable.Type == CSMic.VariableType.Expression;
|
||||
var item = new VariableItem
|
||||
{
|
||||
VariableName = name,
|
||||
Value = valueString,
|
||||
IsExpression = isExpression,
|
||||
ExpressionComputation = null,
|
||||
Icon = IconFont.Variable
|
||||
};
|
||||
|
||||
if (isExpression && !string.IsNullOrWhiteSpace(valueString))
|
||||
{
|
||||
// Compute the expression-based variable's current value
|
||||
_interpreter.Interpret(valueString);
|
||||
item.ExpressionComputation = _interpreter.StringValue ?? string.Empty;
|
||||
item.Icon = IconFont.Function;
|
||||
}
|
||||
|
||||
variables.Add(item);
|
||||
}
|
||||
|
||||
return new InterpretResult
|
||||
{
|
||||
Output = output,
|
||||
Variables = variables
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
17
src/AdvancedCalculator/Services/ICalculatorService.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedCalculator.Models;
|
||||
|
||||
namespace AdvancedCalculator.Services;
|
||||
|
||||
public interface ICalculatorService
|
||||
{
|
||||
Task<InterpretResult> InterpretAsync(string input);
|
||||
}
|
||||
|
||||
public class InterpretResult
|
||||
{
|
||||
public string Output { get; set; } = string.Empty;
|
||||
public IReadOnlyList<VariableItem> Variables { get; set; } = new List<VariableItem>();
|
||||
}
|
||||
|
15
src/AdvancedCalculator/Styles/Buttons.axaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- Center content inside all buttons -->
|
||||
<Style Selector="Button">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Keep toggle buttons consistent -->
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
</Style>
|
||||
</Styles>
|
||||
|
144
src/AdvancedCalculator/Styles/Colors.axaml
Normal file
|
@ -0,0 +1,144 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<!-- Official palette tokens -->
|
||||
<Color x:Key="CPrimary1">#FF5AC3D6</Color>
|
||||
<Color x:Key="CPrimary2">#FF223544</Color>
|
||||
<Color x:Key="CPrimary3">#FF4E6D84</Color>
|
||||
<Color x:Key="CEmph1">#FFF5A623</Color>
|
||||
<Color x:Key="CEmph2">#FFD94E87</Color>
|
||||
<Color x:Key="CEmph3">#FF49C46D</Color>
|
||||
|
||||
<!-- Text & utilities on dark surfaces -->
|
||||
<Color x:Key="CText">#FFEAF2F7</Color>
|
||||
|
||||
<!-- Core brushes -->
|
||||
<SolidColorBrush x:Key="Brush.Primary1" Color="{DynamicResource CPrimary1}"/>
|
||||
<SolidColorBrush x:Key="Brush.Primary2" Color="{DynamicResource CPrimary2}"/>
|
||||
<SolidColorBrush x:Key="Brush.Primary3" Color="{DynamicResource CPrimary3}"/>
|
||||
<SolidColorBrush x:Key="Brush.Emph1" Color="{DynamicResource CEmph1}"/>
|
||||
<SolidColorBrush x:Key="Brush.Emph2" Color="{DynamicResource CEmph2}"/>
|
||||
<SolidColorBrush x:Key="Brush.Emph3" Color="{DynamicResource CEmph3}"/>
|
||||
|
||||
<!-- Surfaces & text -->
|
||||
<SolidColorBrush x:Key="Brush.Surface" Color="{DynamicResource CPrimary2}"/>
|
||||
<SolidColorBrush x:Key="Brush.Background" Color="{DynamicResource CPrimary3}"/>
|
||||
<SolidColorBrush x:Key="Brush.Foreground" Color="{DynamicResource CText}"/>
|
||||
<SolidColorBrush x:Key="Brush.ForegroundOnPrimary">#FF223544</SolidColorBrush>
|
||||
|
||||
<!-- Overlays & borders (use opacity for #RRGGBBAA spec) -->
|
||||
<SolidColorBrush x:Key="Brush.AccentSoft" Color="{DynamicResource CPrimary1}" Opacity="0.16"/>
|
||||
<SolidColorBrush x:Key="Brush.AccentSofter" Color="{DynamicResource CPrimary1}" Opacity="0.10"/>
|
||||
<SolidColorBrush x:Key="Brush.Border" Color="#FFFFFFFF" Opacity="0.18"/>
|
||||
|
||||
<!-- Inputs on dark -->
|
||||
<SolidColorBrush x:Key="Brush.InputBackground" Color="#FFFFFFFF" Opacity="0.06"/>
|
||||
<SolidColorBrush x:Key="Brush.InputBorder" Color="#FFFFFFFF" Opacity="0.22"/>
|
||||
|
||||
<!-- Accent + theme integration (FluentTheme consumes these) -->
|
||||
<SolidColorBrush x:Key="ThemeAccentBrush" Color="{DynamicResource CPrimary1}"/>
|
||||
<SolidColorBrush x:Key="ThemeBackgroundBrush" Color="{DynamicResource CPrimary3}"/>
|
||||
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource CText}"/>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Window background -->
|
||||
<Style Selector="Window">
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Default text color across the app -->
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Primary buttons themed with accent color -->
|
||||
<Style Selector="Button">
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.ForegroundOnPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border}"/>
|
||||
</Style>
|
||||
<Style Selector="Button:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Primary1}"/>
|
||||
<Setter Property="Opacity" Value="0.95"/>
|
||||
</Style>
|
||||
<Style Selector="Button:pressed">
|
||||
<Setter Property="Opacity" Value="0.88"/>
|
||||
</Style>
|
||||
|
||||
<!-- Emphasis button variants (opt-in via classes) -->
|
||||
<Style Selector="Button.success">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Emph3}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.ForegroundOnPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border}"/>
|
||||
</Style>
|
||||
<Style Selector="Button.warning">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Emph1}"/>
|
||||
<Setter Property="Foreground" Value="#FF222222"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border}"/>
|
||||
</Style>
|
||||
<Style Selector="Button.danger">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Emph2}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.ForegroundOnPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border}"/>
|
||||
</Style>
|
||||
|
||||
<!-- ToggleButton aligned with Button -->
|
||||
<Style Selector="ToggleButton">
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.ForegroundOnPrimary}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.Border}"/>
|
||||
</Style>
|
||||
<Style Selector="ToggleButton:checked">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Primary3}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Simple card-like border using surface overlay -->
|
||||
<Style Selector="Border.card">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Surface}"/>
|
||||
<Setter Property="CornerRadius" Value="8"/>
|
||||
<Setter Property="Padding" Value="8"/>
|
||||
</Style>
|
||||
|
||||
<!-- Inputs (dark) -->
|
||||
<Style Selector="TextBox">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.InputBackground}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource Brush.InputBorder}"/>
|
||||
</Style>
|
||||
<Style Selector="TextBox:focus">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeAccentBrush}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Lists & selection -->
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.AccentSofter}"/>
|
||||
</Style>
|
||||
<Style Selector="ListBoxItem:selected">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.AccentSoft}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeAccentBrush}"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
</Style>
|
||||
|
||||
<!-- Menus & flyouts -->
|
||||
<Style Selector="ContextMenu">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Surface}"/>
|
||||
</Style>
|
||||
<Style Selector="MenuItem:pointerover">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.AccentSofter}"/>
|
||||
</Style>
|
||||
<Style Selector="FlyoutPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource Brush.Surface}"/>
|
||||
</Style>
|
||||
|
||||
<!-- Scrollbars -->
|
||||
<Style Selector="ScrollBar">
|
||||
<Setter Property="Foreground" Value="{DynamicResource Brush.Primary1}"/>
|
||||
<Setter Property="Background" Value="#00000000"/>
|
||||
</Style>
|
||||
</Styles>
|
16
src/AdvancedCalculator/Styles/Scroll.axaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<!-- Shared scrolling defaults -->
|
||||
<Style Selector="ListBox">
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
|
||||
</Style>
|
||||
|
||||
<!-- Thin-ish scrollbars by default; platform overrides will adjust -->
|
||||
<Style Selector="ScrollBar:vertical">
|
||||
<Setter Property="Width" Value="10"/>
|
||||
</Style>
|
||||
<Style Selector="ScrollBar:horizontal">
|
||||
<Setter Property="Height" Value="10"/>
|
||||
</Style>
|
||||
</Styles>
|
30
src/AdvancedCalculator/Styles/Typography.axaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Styles.Resources>
|
||||
<!-- Shared typography resources -->
|
||||
<x:Double x:Key="FontSizeSmall">12</x:Double>
|
||||
<x:Double x:Key="FontSizeBase">14</x:Double>
|
||||
<x:Double x:Key="FontSizeLarge">16</x:Double>
|
||||
<x:Double x:Key="FontSizeXL">20</x:Double>
|
||||
|
||||
<x:Double x:Key="IconSizeS">18</x:Double>
|
||||
<x:Double x:Key="IconSizeM">22</x:Double>
|
||||
<x:Double x:Key="IconSizeL">26</x:Double>
|
||||
|
||||
<x:Double x:Key="LineHeightTight">1.1</x:Double>
|
||||
<x:Double x:Key="LineHeightBase">1.25</x:Double>
|
||||
<x:Double x:Key="LineHeightRelaxed">1.4</x:Double>
|
||||
</Styles.Resources>
|
||||
|
||||
<!-- Defaults for text -->
|
||||
<Style Selector="TextBlock">
|
||||
<Setter Property="TextElement.FontSize" Value="{DynamicResource FontSizeBase}"/>
|
||||
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||
</Style>
|
||||
|
||||
<!-- Subtle caption text helper -->
|
||||
<Style Selector="TextBlock.caption">
|
||||
<Setter Property="TextElement.FontSize" Value="{DynamicResource FontSizeSmall}"/>
|
||||
<Setter Property="Opacity" Value="0.85"/>
|
||||
</Style>
|
||||
</Styles>
|
165
src/AdvancedCalculator/ViewModels/MainViewModel.cs
Normal file
|
@ -0,0 +1,165 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using AdvancedCalculator.Models;
|
||||
using AdvancedCalculator.Services;
|
||||
using CSMic;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AdvancedCalculator.ViewModels;
|
||||
|
||||
public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ICalculatorService _calculatorService;
|
||||
public event EventHandler<string>? CopyRequested;
|
||||
|
||||
public MainViewModel()
|
||||
: this(new CalculatorService())
|
||||
{
|
||||
}
|
||||
|
||||
public MainViewModel(ICalculatorService calculatorService)
|
||||
{
|
||||
_calculatorService = calculatorService;
|
||||
History = new ObservableCollection<HistoryItem>();
|
||||
Variables = new ObservableCollection<VariableItem>();
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
private string _inputText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isFunctionsPanelOpen;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _selectedHistoryIndex = -1;
|
||||
|
||||
// Caret position in the input TextBox
|
||||
[ObservableProperty]
|
||||
private int _caretIndex;
|
||||
|
||||
public ObservableCollection<HistoryItem> History { get; }
|
||||
public ObservableCollection<VariableItem> Variables { get; }
|
||||
|
||||
// App version strings
|
||||
private static readonly string s_appVersion =
|
||||
typeof(MainViewModel).Assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion ?? "0.0.0";
|
||||
|
||||
private static string s_appVersionShort
|
||||
{
|
||||
get
|
||||
{
|
||||
var m = Regex.Match(
|
||||
s_appVersion?.Trim() ?? "",
|
||||
@"^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?=$|\+)"
|
||||
);
|
||||
if (!m.Success) return s_appVersion ?? "Unknown Version";
|
||||
|
||||
// Return exactly what’s present (major | major.minor | major.minor.patch)
|
||||
return m.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public string AppVersion => s_appVersion;
|
||||
public string AppVersionShort => s_appVersionShort;
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleFunctions()
|
||||
{
|
||||
IsFunctionsPanelOpen = !IsFunctionsPanelOpen;
|
||||
}
|
||||
|
||||
// Insert helpers for touch: appends tokens to the input box
|
||||
[RelayCommand]
|
||||
private void InsertVariable(VariableItem variableItem)
|
||||
{
|
||||
if (variableItem == null) return;
|
||||
if (string.IsNullOrWhiteSpace(variableItem.VariableName))
|
||||
return;
|
||||
InsertToken(variableItem.VariableName);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void InsertFunction(FunctionDefinitionItem functionDefintionItem)
|
||||
{
|
||||
if (functionDefintionItem == null) return;
|
||||
if (string.IsNullOrWhiteSpace(functionDefintionItem.FunctionName))
|
||||
return;
|
||||
InsertToken(functionDefintionItem.FunctionName + "()");
|
||||
// Place caret inside the parentheses
|
||||
if (!string.IsNullOrEmpty(InputText))
|
||||
{
|
||||
CaretIndex = Math.Max(0, InputText.Length - 1);
|
||||
}
|
||||
// Close the functions flyout after insertion for smoother UX
|
||||
IsFunctionsPanelOpen = false;
|
||||
}
|
||||
|
||||
private void InsertToken(string token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(InputText))
|
||||
{
|
||||
InputText = token;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!char.IsWhiteSpace(InputText[^1]))
|
||||
InputText += " ";
|
||||
|
||||
InputText += token;
|
||||
}
|
||||
|
||||
[RelayCommand(AllowConcurrentExecutions = false)]
|
||||
private async Task Submit()
|
||||
{
|
||||
var text = InputText?.Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
var result = await _calculatorService.InterpretAsync(text);
|
||||
|
||||
History.Add(new HistoryItem { Input = text, Output = result.Output });
|
||||
|
||||
Variables.Clear();
|
||||
foreach (var v in result.Variables)
|
||||
{
|
||||
Variables.Add(v);
|
||||
}
|
||||
|
||||
InputText = string.Empty;
|
||||
SelectedHistoryIndex = History.Count - 1;
|
||||
}
|
||||
|
||||
// Copy helpers (MVVM-pure): build text in VM, View handles clipboard via CopyRequested
|
||||
[RelayCommand]
|
||||
private void CopyHistoryInput(HistoryItem? item)
|
||||
{
|
||||
if (item is null || string.IsNullOrWhiteSpace(item.Input)) return;
|
||||
CopyRequested?.Invoke(this, item.Input);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyHistoryOutput(HistoryItem? item)
|
||||
{
|
||||
if (item is null || string.IsNullOrWhiteSpace(item.Output)) return;
|
||||
CopyRequested?.Invoke(this, item.Output);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyHistoryBoth(HistoryItem? item)
|
||||
{
|
||||
if (item is null) return;
|
||||
var input = item.Input?.Trim();
|
||||
var output = item.Output?.Trim();
|
||||
if (string.IsNullOrEmpty(input) && string.IsNullOrEmpty(output)) return;
|
||||
if (string.IsNullOrEmpty(input)) { CopyRequested?.Invoke(this, output!); return; }
|
||||
if (string.IsNullOrEmpty(output)) { CopyRequested?.Invoke(this, input!); return; }
|
||||
|
||||
CopyRequested?.Invoke(this, $"{input} = {output}");
|
||||
}
|
||||
}
|
7
src/AdvancedCalculator/ViewModels/ViewModelBase.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace AdvancedCalculator.ViewModels;
|
||||
|
||||
public class ViewModelBase : ObservableObject
|
||||
{
|
||||
}
|
235
src/AdvancedCalculator/Views/MainView.axaml
Normal file
|
@ -0,0 +1,235 @@
|
|||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="clr-namespace:AdvancedCalculator.ViewModels"
|
||||
xmlns:m="clr-namespace:AdvancedCalculator.Models"
|
||||
xmlns:conv="clr-namespace:AdvancedCalculator.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="900" d:DesignHeight="600"
|
||||
x:Class="AdvancedCalculator.Views.MainView"
|
||||
x:DataType="vm:MainViewModel"
|
||||
x:Name="Root">
|
||||
<UserControl.Resources>
|
||||
<conv:BoolToGridLengthConverter x:Key="BoolToGridLengthConverter" />
|
||||
<conv:WidthToBooleanConverter x:Key="WidthToBooleanConverter" />
|
||||
<conv:IsZeroConverter x:Key="IsZeroConverter" />
|
||||
<conv:WidthToSplitViewModeConverter x:Key="WidthToSplitViewModeConverter" />
|
||||
<conv:WidthToPaneOpenConverter x:Key="WidthToPaneOpenConverter" />
|
||||
<conv:CategoryToBrushConverter x:Key="CategoryToBrushConverter" />
|
||||
</UserControl.Resources>
|
||||
<!-- Replace columns with a responsive SplitView -->
|
||||
<SplitView x:Name="RootSplit"
|
||||
OpenPaneLength="320"
|
||||
CompactPaneLength="0"
|
||||
DisplayMode="{Binding $parent[Window].Bounds.Width, Converter={StaticResource WidthToSplitViewModeConverter}, ConverterParameter=640}"
|
||||
IsPaneOpen="{Binding $parent[Window].Bounds.Width, Converter={StaticResource WidthToPaneOpenConverter}, ConverterParameter=640}">
|
||||
|
||||
<!-- Left pane: Variables only (Functions moved to flyout) -->
|
||||
<SplitView.Pane>
|
||||
<Grid Background="{DynamicResource Brush.Surface}"
|
||||
RowDefinitions="*,Auto">
|
||||
|
||||
<!-- Variables list with empty-state hint overlay -->
|
||||
<Grid Grid.Row="0">
|
||||
<ListBox ItemsSource="{Binding Variables}" SelectedIndex="-1"
|
||||
AutomationProperties.Name="Variables list">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:VariableItem">
|
||||
<Button x:DataType="vm:MainViewModel"
|
||||
DataContext="{Binding #Root.DataContext}"
|
||||
Command="{Binding InsertVariableCommand}"
|
||||
CommandParameter="{Binding $parent[ListBoxItem].DataContext}"
|
||||
Background="Transparent" BorderThickness="0" Padding="8" MinHeight="44"
|
||||
AutomationProperties.Name="Insert variable">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||
DataContext="{Binding $parent[ListBoxItem].DataContext}"
|
||||
x:DataType="m:VariableItem">
|
||||
<TextBlock Grid.Column="0" FontFamily="{StaticResource MDI}"
|
||||
FontSize="{DynamicResource IconSizeM}" Text="{Binding Icon}" Margin="0,0,8,0" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding VariableName}" FontWeight="Bold" />
|
||||
<StackPanel Grid.Column="2" Spacing="6">
|
||||
<TextBlock Text="{Binding Value}" />
|
||||
<TextBlock IsVisible="{Binding IsExpression}" Text="{Binding ExpressionComputation}" FontStyle="Italic" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
|
||||
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<TextBlock Text="No variables yet. Define with: x :: 5 or y := x * 2"
|
||||
IsVisible="{Binding Variables.Count, Converter={StaticResource IsZeroConverter}}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0.6"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom sticky version bar -->
|
||||
<Border Grid.Row="1" Padding="10" Background="{DynamicResource Brush.Surface}"
|
||||
BorderBrush="{DynamicResource Brush.Border}" BorderThickness="0,1,0,0">
|
||||
<TextBlock Text="{Binding AppVersionShort, StringFormat='Version {0}'}"
|
||||
HorizontalAlignment="Left"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</SplitView.Pane>
|
||||
|
||||
<!-- Right content: History + Input -->
|
||||
<SplitView.Content>
|
||||
<Grid RowDefinitions="*,Auto">
|
||||
<!-- History -->
|
||||
<Grid Grid.Row="0" Background="{DynamicResource ThemeBackgroundBrush}">
|
||||
<ListBox x:Name="HistoryList" ItemsSource="{Binding History}" SelectedIndex="{Binding SelectedHistoryIndex}"
|
||||
AutomationProperties.Name="History list">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:HistoryItem">
|
||||
<!-- Entire row UI; ListBox-level Tapped handler triggers copy -->
|
||||
<Grid x:Name="HistoryRow" ColumnDefinitions="Auto,*,Auto" Margin="4,2" Background="Transparent"
|
||||
DataContext="{Binding $parent[ListBoxItem].DataContext}"
|
||||
x:DataType="m:HistoryItem">
|
||||
<TextBlock Grid.Column="0" FontFamily="{StaticResource MDI}" Text="{x:Static m:IconFont.Abacus}"
|
||||
FontSize="{DynamicResource IconSizeM}" VerticalAlignment="Center" Margin="0,0,8,0" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="{Binding Input}" MaxLines="3" />
|
||||
<TextBlock Text="{Binding Output}" FontWeight="Bold" MaxLines="2" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Context menu for right-click / long-press (touch) -->
|
||||
<Grid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:DataType="vm:MainViewModel"
|
||||
DataContext="{Binding #Root.DataContext}"
|
||||
Header="Copy Input"
|
||||
Command="{Binding CopyHistoryInputCommand}"
|
||||
CommandParameter="{Binding #HistoryRow.DataContext}">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock FontFamily="{StaticResource MDI}"
|
||||
FontSize="{DynamicResource IconSizeS}"
|
||||
Text="{x:Static m:IconFont.DiceMultipleOutline}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:DataType="vm:MainViewModel"
|
||||
DataContext="{Binding #Root.DataContext}"
|
||||
Header="Copy Output"
|
||||
Command="{Binding CopyHistoryOutputCommand}"
|
||||
CommandParameter="{Binding #HistoryRow.DataContext}">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock FontFamily="{StaticResource MDI}"
|
||||
FontSize="{DynamicResource IconSizeS}"
|
||||
Text="{x:Static m:IconFont.DiceMultipleOutline}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem x:DataType="vm:MainViewModel"
|
||||
DataContext="{Binding #Root.DataContext}"
|
||||
Header="Copy Input = Output"
|
||||
Command="{Binding CopyHistoryBothCommand}"
|
||||
CommandParameter="{Binding #HistoryRow.DataContext}">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock FontFamily="{StaticResource MDI}"
|
||||
FontSize="{DynamicResource IconSizeS}"
|
||||
Text="{x:Static m:IconFont.DiceMultipleOutline}"/>
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Grid.ContextMenu>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<TextBlock Text="No history yet. Enter an expression and Evaluate."
|
||||
IsVisible="{Binding History.Count, Converter={StaticResource IsZeroConverter}}"
|
||||
IsHitTestVisible="False"
|
||||
Opacity="0.6"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<!-- Input Row -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*,Auto" Margin="4">
|
||||
<!-- Pane toggle: visible on narrow only -->
|
||||
<ToggleButton Margin="0,0,6,0"
|
||||
IsVisible="{Binding $parent[Window].Bounds.Width,
|
||||
Converter={StaticResource WidthToBooleanConverter},
|
||||
ConverterParameter=640}"
|
||||
IsChecked="{Binding #RootSplit.IsPaneOpen}"
|
||||
AutomationProperties.Name="Open side panel">
|
||||
<TextBlock Text="≡" FontSize="18"/>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- Functions flyout anchored to this button -->
|
||||
<Button Grid.Column="1" Command="{Binding ToggleFunctionsCommand}" Margin="0,0,6,0" MinHeight="40" MinWidth="40"
|
||||
AutomationProperties.Name="Toggle functions panel">
|
||||
<Button.Flyout>
|
||||
<Flyout
|
||||
IsOpen="{Binding IsFunctionsPanelOpen, Mode=TwoWay}"
|
||||
Placement="BottomEdgeAlignedLeft">
|
||||
<Border x:Name="FunctionsPanelRoot" Padding="8" MinWidth="360" MaxHeight="420"
|
||||
Background="{DynamicResource Brush.Surface}" BorderBrush="{DynamicResource Brush.Border}" BorderThickness="1" CornerRadius="6">
|
||||
<ScrollViewer>
|
||||
<StackPanel>
|
||||
<ItemsControl ItemsSource="{x:Static m:FunctionDefinitionItem.DefinedFunctionGroups}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:FunctionGroup">
|
||||
<StackPanel Margin="0,0,0,8">
|
||||
<!-- Group Header -->
|
||||
<Border Background="{Binding Category, Converter={StaticResource CategoryToBrushConverter}}"
|
||||
CornerRadius="6" Padding="8" Margin="0,0,0,6">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||
<TextBlock FontFamily="{StaticResource MDI}" Text="{Binding Icon}"/>
|
||||
<TextBlock Text="{Binding GroupName}" FontWeight="Bold"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Group Items: simple rows -->
|
||||
<ListBox ItemsSource="{Binding Functions}" BorderThickness="0" SelectedIndex="-1">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:FunctionDefinitionItem">
|
||||
<Border Background="Transparent" Padding="8" MinHeight="36"
|
||||
AutomationProperties.Name="Insert function"
|
||||
Tapped="OnFunctionsItemTapped">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding FunctionName}" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding FunctionDescription}" Opacity="0.8"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
<TextBlock FontFamily="{StaticResource MDI}" Text="{x:Static m:IconFont.Function}" />
|
||||
</Button>
|
||||
|
||||
<TextBox x:Name="InputBox" Grid.Column="2" Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
CaretIndex="{Binding CaretIndex, Mode=TwoWay}"
|
||||
MinHeight="44"
|
||||
AutomationProperties.Name="Expression input"
|
||||
Watermark="Type an expression (e.g., 2 + 2)">
|
||||
<InputElement.KeyBindings>
|
||||
<KeyBinding Gesture="Enter" Command="{Binding SubmitCommand}" />
|
||||
</InputElement.KeyBindings>
|
||||
</TextBox>
|
||||
|
||||
<!-- Explicit evaluate button for touch -->
|
||||
<Button Grid.Column="3" Command="{Binding SubmitCommand}" MinHeight="44" Padding="16,8"
|
||||
AutomationProperties.Name="Evaluate">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||
<TextBlock FontFamily="{StaticResource MDI}"
|
||||
FontSize="{DynamicResource IconSizeM}"
|
||||
Text="{x:Static m:IconFont.SquareRoot}"/>
|
||||
<TextBlock Text="Evaluate"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</SplitView.Content>
|
||||
</SplitView>
|
||||
</UserControl>
|
105
src/AdvancedCalculator/Views/MainView.axaml.cs
Normal file
|
@ -0,0 +1,105 @@
|
|||
using System;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
using AdvancedCalculator.ViewModels;
|
||||
using AdvancedCalculator.Models;
|
||||
using Avalonia.Controls.Presenters;
|
||||
|
||||
namespace AdvancedCalculator.Views;
|
||||
|
||||
public partial class MainView : UserControl
|
||||
{
|
||||
public MainView()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.AttachedToVisualTree += OnAttachedToVisualTree;
|
||||
this.DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||
}
|
||||
|
||||
private MainViewModel? _vm;
|
||||
private ListBox? _historyList;
|
||||
// Functions flyout is handled via per-item Tapped in XAML
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_vm = DataContext as MainViewModel;
|
||||
if (_vm is not null)
|
||||
{
|
||||
_vm.CopyRequested += OnCopyRequested;
|
||||
}
|
||||
|
||||
_historyList = this.FindControl<ListBox>("HistoryList");
|
||||
if (_historyList is not null)
|
||||
{
|
||||
// Handle taps anywhere in a history row to copy output text
|
||||
_historyList.AddHandler(InputElement.TappedEvent, OnHistoryTapped,
|
||||
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);
|
||||
}
|
||||
|
||||
// Functions flyout wiring is done via Opened/Closed events
|
||||
}
|
||||
|
||||
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
if (_vm is not null)
|
||||
{
|
||||
_vm.CopyRequested -= OnCopyRequested;
|
||||
}
|
||||
if (_historyList is not null)
|
||||
{
|
||||
_historyList.RemoveHandler(InputElement.TappedEvent, OnHistoryTapped);
|
||||
_historyList = null;
|
||||
}
|
||||
_vm = null;
|
||||
}
|
||||
|
||||
private async void OnCopyRequested(object? sender, string text)
|
||||
{
|
||||
var top = TopLevel.GetTopLevel(this);
|
||||
if (top?.Clipboard is null) return;
|
||||
try
|
||||
{
|
||||
await top.Clipboard.SetTextAsync(text);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore clipboard errors; e.g., browser permission or missing gesture context
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHistoryTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
// Find the ListBoxItem the tap originated from
|
||||
var source = e.Source as ContentPresenter;
|
||||
var container = source?.FindAncestorOfType<ListBoxItem>();
|
||||
if (container?.DataContext is not HistoryItem item)
|
||||
return;
|
||||
|
||||
var vm = DataContext as MainViewModel;
|
||||
if (vm?.CopyHistoryOutputCommand.CanExecute(item) == true)
|
||||
{
|
||||
vm.CopyHistoryOutputCommand.Execute(item);
|
||||
// Mark handled to avoid unintended selection change on tap
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFunctionsItemTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
var lbi = (e.Source as Control)?.FindAncestorOfType<ListBoxItem>();
|
||||
if (lbi?.DataContext is FunctionDefinitionItem item &&
|
||||
_vm?.InsertFunctionCommand.CanExecute(item) == true)
|
||||
{
|
||||
_vm.InsertFunctionCommand.Execute(item);
|
||||
// Return focus to the input box so the caret is active
|
||||
var input = this.FindControl<TextBox>("InputBox");
|
||||
input?.Focus();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Removed Opened/Closed dynamic wiring; handled directly in XAML.
|
||||
}
|
12
src/AdvancedCalculator/Views/MainWindow.axaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:AdvancedCalculator.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="clr-namespace:AdvancedCalculator.Views"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="AdvancedCalculator.Views.MainWindow"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="AdvancedCalculator">
|
||||
<views:MainView />
|
||||
</Window>
|
11
src/AdvancedCalculator/Views/MainWindow.axaml.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Avalonia.Controls;
|
||||
|
||||
namespace AdvancedCalculator.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -1,9 +0,0 @@
|
|||
<Application x:Class="AdvancedCalculator.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:AdvancedCalculator"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<FontFamily x:Key="MDI">pack://application:,,,/#Material Design Icons</FontFamily>
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -1,17 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace AdvancedCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
32
src/Directory.Build.props
Normal file
|
@ -0,0 +1,32 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<Nullable>enable</Nullable>
|
||||
<AvaloniaVersion>11.1.0</AvaloniaVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Centralized app versioning for all projects -->
|
||||
<PropertyGroup Label="Versioning">
|
||||
<!-- Single source of truth: update these -->
|
||||
<AppVersion>1.2.0</AppVersion>
|
||||
<PrereleaseLabel></PrereleaseLabel>
|
||||
|
||||
<!-- Derived semantic version pieces -->
|
||||
<AppVersionMajor>$([System.Text.RegularExpressions.Regex]::Match('$(AppVersion)', '^(\d+)\.(\d+)\.(\d+)').Groups[1].Value)</AppVersionMajor>
|
||||
<AppVersionMinor>$([System.Text.RegularExpressions.Regex]::Match('$(AppVersion)', '^(\d+)\.(\d+)\.(\d+)').Groups[2].Value)</AppVersionMinor>
|
||||
<AppVersionPatch>$([System.Text.RegularExpressions.Regex]::Match('$(AppVersion)', '^(\d+)\.(\d+)\.(\d+)').Groups[3].Value)</AppVersionPatch>
|
||||
|
||||
<!-- Standard .NET version properties -->
|
||||
<VersionPrefix>$(AppVersion)</VersionPrefix>
|
||||
<VersionSuffix Condition=" '$(PrereleaseLabel)' != '' ">$(PrereleaseLabel)</VersionSuffix>
|
||||
<Version Condition=" '$(PrereleaseLabel)' == '' ">$(VersionPrefix)</Version>
|
||||
<Version Condition=" '$(PrereleaseLabel)' != '' ">$(VersionPrefix)-$(VersionSuffix)</Version>
|
||||
<AssemblyVersion>$(AppVersionMajor).$(AppVersionMinor).0.0</AssemblyVersion>
|
||||
<FileVersion>$(AppVersionMajor).$(AppVersionMinor).$(AppVersionPatch).0</FileVersion>
|
||||
<InformationalVersion>$(Version)</InformationalVersion>
|
||||
|
||||
<!-- Android mapping: versionName + versionCode -->
|
||||
<ApplicationDisplayVersion>$(Version)</ApplicationDisplayVersion>
|
||||
<!-- VersionCode = major*10000 + minor*100 + patch (e.g., 1.2.3 -> 10203) -->
|
||||
<ApplicationVersion>$([MSBuild]::Add($([MSBuild]::Multiply($(AppVersionMajor), 10000)),$([MSBuild]::Add($([MSBuild]::Multiply($(AppVersionMinor), 100)),$(AppVersionPatch)))))</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -1,40 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace AdvancedCalculator
|
||||
{
|
||||
public class FunctionDefinitonItem
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string FunctionName { get; private set; }
|
||||
|
||||
public string FunctionDescription { get; private set; }
|
||||
|
||||
public string Icon { get; private set; }
|
||||
|
||||
public IEnumerable<KeyValuePair<string, string>> FunctionArguments { get; private set; }
|
||||
|
||||
public static IEnumerable<FunctionDefinitonItem> DefinedFunctions
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "sin", Icon = IconFont.SineWave, FunctionDescription = "Returns the sine value of a given expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "cos", Icon = IconFont.CosineWave, FunctionDescription = "Returns the cosine value of a given expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "tan", Icon = IconFont.MathTan, FunctionDescription = "Returns the tangent value of a given expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "round", Icon = IconFont.RoundedCorner, FunctionDescription = "Rounds an expression to the nearest whole number.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "sqrt", Icon = IconFont.SquareRoot, FunctionDescription = "Returns the square root of a given expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "abs", Icon = IconFont.PlusCircle, FunctionDescription = "Returns the absolute value of a given expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("expression", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "exp", Icon = IconFont.AlphaECircle, FunctionDescription = "Returns the constant e to a given power.", FunctionArguments = new[] { new KeyValuePair<string, string>("power", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "log", Icon = IconFont.MathLog, FunctionDescription = "Returns the log of the first expression to the base of the second expression.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "An expression to compute."), new KeyValuePair<string, string>("base", "An expression to compute.") } };
|
||||
yield return new FunctionDefinitonItem() { FunctionName = "precision", Icon = IconFont.DecimalIncrease, FunctionDescription = "Returns the value of expression1 to a given precision. For example, precision(12.3456789, 4) will return 12.3456.", FunctionArguments = new[] { new KeyValuePair<string, string>("value", "An expression to compute."), new KeyValuePair<string, string>("precision", "An expression to compute.") } };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Properties
|
||||
|
||||
private FunctionDefinitonItem()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AdvancedCalculator
|
||||
{
|
||||
public class HistoryItem
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Input { get; set; }
|
||||
|
||||
public string Output { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
5341
src/IconFont.cs
|
@ -1,126 +0,0 @@
|
|||
<Window x:Class="AdvancedCalculator.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:AdvancedCalculator"
|
||||
mc:Ignorable="d"
|
||||
Title="Advanced Calculator" Height="450" Width="800" Icon="calculator-variant-black.png">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="3*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition x:Name="rdFxDefinitions" Height="0" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListView Grid.Row="0" x:Name="lvVariableList" HorizontalContentAlignment="Stretch">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" FontFamily="{StaticResource MDI}" FontSize="24" Text="{Binding Icon}" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding VariableName}" FontWeight="Bold" />
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="{Binding Value}" />
|
||||
<TextBlock Visibility="{Binding ExpressionVisibility}" Text="{Binding ExpressionComputation}" FontStyle="Italic" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<ListView Grid.Row="1" x:Name="lvFxDefinitions" ItemsSource="{x:Static local:FunctionDefinitonItem.DefinedFunctions}">
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListViewItem}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListViewItem}">
|
||||
<ContentPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock FontFamily="{StaticResource MDI}" Text="{Binding Icon}" Margin="5,0" />
|
||||
<TextBlock Text="{Binding FunctionName}" FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding FunctionDescription}" FontStyle="Italic" />
|
||||
<ListView Margin="5,0" ItemsSource="{Binding FunctionArguments}" BorderThickness="0">
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ListViewItem}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListViewItem}">
|
||||
<ContentPresenter />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Key}" />
|
||||
<TextBlock Text="{Binding Value}" FontStyle="Italic" Margin="5,0,0,0" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
<GridSplitter Grid.Column="0" HorizontalAlignment="Right" Height="100" VerticalAlignment="Center" Width="5" />
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<ListView Grid.Row="0" x:Name="lvHistory">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel VerticalAlignment="Bottom" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock FontFamily="{StaticResource MDI}" Text="{x:Static local:IconFont.ArrowRightDropCircle}" FontSize="24" VerticalAlignment="Center" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="{Binding Input}" />
|
||||
<TextBlock Text="{Binding Output}" FontWeight="Bold" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button x:Name="btnFx" Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" Margin="5" Click="btnFx_Click">
|
||||
<TextBlock FontFamily="{StaticResource MDI}" Text="{x:Static local:IconFont.FunctionVariant}" />
|
||||
</Button>
|
||||
<TextBox Grid.Column="1" x:Name="txtInput" Margin="10" KeyDown="txtInput_KeyDown" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -1,70 +0,0 @@
|
|||
using csmic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace AdvancedCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private InputInterpreter interpreter = new InputInterpreter();
|
||||
|
||||
private List<HistoryItem> historyItems = new List<HistoryItem>();
|
||||
|
||||
private List<VariableItem> variableItems = new List<VariableItem>();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void txtInput_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if(e.Key == Key.Enter)
|
||||
{
|
||||
var input = this.txtInput.Text;
|
||||
|
||||
await Task.Run(() =>
|
||||
{
|
||||
this.interpreter.Interpret(input);
|
||||
this.historyItems.Add(new HistoryItem() { Input = input, Output = this.interpreter.Output });
|
||||
this.variableItems = this.interpreter.Variables.Select(kp => new VariableItem() { VariableName = kp.Key, Value = kp.Value.Value.ToString(), ExpressionVisibility = kp.Value.Type == VariableType.Equation ? Visibility.Visible : Visibility.Hidden, ExpressionComputation = "", Icon = IconFont.Variable }).ToList();
|
||||
foreach(var expressionVariable in this.variableItems.Where(v => v.ExpressionVisibility == Visibility.Visible))
|
||||
{
|
||||
this.interpreter.Interpret(expressionVariable.Value);
|
||||
expressionVariable.ExpressionComputation = this.interpreter.Output;
|
||||
expressionVariable.Icon = IconFont.Function;
|
||||
}
|
||||
});
|
||||
|
||||
this.txtInput.Clear();
|
||||
|
||||
this.lvHistory.ItemsSource = this.historyItems;
|
||||
this.lvHistory.Items.Refresh();
|
||||
|
||||
this.lvVariableList.ItemsSource = this.variableItems;
|
||||
this.lvVariableList.Items.Refresh();
|
||||
|
||||
this.lvHistory.Items.MoveCurrentToLast();
|
||||
this.lvHistory.ScrollIntoView(this.lvHistory.Items.CurrentItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void btnFx_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(this.rdFxDefinitions.Height.Value == 0)
|
||||
{
|
||||
this.rdFxDefinitions.Height = new GridLength(1, GridUnitType.Star);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rdFxDefinitions.Height = new GridLength(0, GridUnitType.Pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("AdvancedCalculator")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AdvancedCalculator")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
71
src/Properties/Resources.Designer.cs
generated
|
@ -1,71 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace AdvancedCalculator.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AdvancedCalculator.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
30
src/Properties/Settings.Designer.cs
generated
|
@ -1,30 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace AdvancedCalculator.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -1,21 +0,0 @@
|
|||
using System.Windows;
|
||||
|
||||
namespace AdvancedCalculator
|
||||
{
|
||||
public class VariableItem
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string VariableName { get; set; }
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public Visibility ExpressionVisibility { get; set; }
|
||||
|
||||
public string ExpressionComputation { get; set; }
|
||||
|
||||
#endregion Properties
|
||||
}
|
||||
}
|
173
src/build.ps1
Normal file
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
build.ps1 - cross-platform build for Avalonia (Windows exe, Web/WASM, Android APK)
|
||||
Usage:
|
||||
pwsh ./build.ps1 # build all
|
||||
pwsh ./build.ps1 -Targets win # just Windows
|
||||
pwsh ./build.ps1 -Targets web,android # pick targets
|
||||
pwsh ./build.ps1 -Version 1.2.3 # override version (default comes from Directory.Build.props)
|
||||
#>
|
||||
|
||||
param(
|
||||
[string[]]$Targets = @('win','web','android'),
|
||||
[string]$Configuration = 'Release',
|
||||
[string]$Version = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Always anchor to the folder containing this script (repo/src)
|
||||
$Root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
Set-Location $Root
|
||||
|
||||
# --- PROJECT PATHS (relative to repo/src) ---
|
||||
$ProjDesktop = 'AdvancedCalculator.Desktop/AdvancedCalculator.Desktop.csproj'
|
||||
$ProjWeb = 'AdvancedCalculator.Browser/AdvancedCalculator.Browser.csproj'
|
||||
$ProjAndroid = 'AdvancedCalculator.Android/AdvancedCalculator.Android.csproj'
|
||||
|
||||
# --- ARTIFACTS ---
|
||||
$Artifacts = Join-Path $Root 'artifacts'
|
||||
$OutWin = Join-Path $Artifacts 'win-x64'
|
||||
$OutWeb = Join-Path $Artifacts 'web'
|
||||
$OutDroid = Join-Path $Artifacts 'android'
|
||||
New-Item -ItemType Directory -Force -Path $Artifacts,$OutWin,$OutWeb,$OutDroid | Out-Null
|
||||
|
||||
# --- Resolve dotnet exe once ---
|
||||
$DotnetExe = (Get-Command dotnet -CommandType Application).Source
|
||||
|
||||
function Invoke-Dotnet {
|
||||
param([Parameter(ValueFromRemainingArguments = $true)][string[]]$Args)
|
||||
Write-Host ">> dotnet $($Args -join ' ')" -ForegroundColor Cyan
|
||||
& $DotnetExe @Args
|
||||
$code = $LASTEXITCODE
|
||||
if ($code -ne 0) { throw "dotnet exited with $code" }
|
||||
}
|
||||
|
||||
# Run a dotnet command from within the project's directory
|
||||
function Invoke-InProjectDir {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$ProjectPath,
|
||||
[Parameter(ValueFromRemainingArguments = $true)][string[]]$Args
|
||||
)
|
||||
$projDir = Split-Path -Parent $ProjectPath
|
||||
$projFile = Split-Path -Leaf $ProjectPath
|
||||
Push-Location $projDir
|
||||
try {
|
||||
Invoke-Dotnet @Args $projFile
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
# --- Version (default from Directory.Build.props unless overridden) ---
|
||||
function Get-VersionFromProps {
|
||||
param([Parameter(Mandatory=$true)][string]$PropsPath)
|
||||
if (-not (Test-Path $PropsPath)) { return $null }
|
||||
try {
|
||||
$xml = [xml](Get-Content -Raw -Path $PropsPath)
|
||||
$groups = @($xml.Project.PropertyGroup)
|
||||
$app = ($groups | ForEach-Object { $_.AppVersion } | Where-Object { $_ -and $_.Trim() -ne '' } | Select-Object -First 1)
|
||||
$pre = ($groups | ForEach-Object { $_.PrereleaseLabel } | Where-Object { $_ -and $_.Trim() -ne '' } | Select-Object -First 1)
|
||||
if ($app) {
|
||||
if ($pre) { return "$app-$pre" }
|
||||
return $app
|
||||
}
|
||||
} catch { }
|
||||
return $null
|
||||
}
|
||||
|
||||
$ExplicitVersion = $false
|
||||
if ($PSBoundParameters.ContainsKey('Version') -and $Version) { $ExplicitVersion = $true }
|
||||
|
||||
$DerivedVersion = $null
|
||||
if (-not $ExplicitVersion) {
|
||||
$DerivedVersion = Get-VersionFromProps (Join-Path $Root 'Directory.Build.props')
|
||||
}
|
||||
|
||||
if ($ExplicitVersion) {
|
||||
# Normalize to SemVer with 3 numeric segments (X.Y.Z)
|
||||
if ($Version -match '^(\d+)\.(\d+)$') { $Version = "$Version.0" }
|
||||
Write-Host "Using Version (override): $Version" -ForegroundColor Yellow
|
||||
} elseif ($DerivedVersion) {
|
||||
if ($DerivedVersion -match '^(\d+)\.(\d+)$') { $DerivedVersion = "$DerivedVersion.0" }
|
||||
Write-Host "Using Version (from Directory.Build.props): $DerivedVersion" -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "No explicit version provided; MSBuild will compute Version from Directory.Build.props." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# --- Ensure SDK/workloads present ---
|
||||
Invoke-Dotnet --info | Out-Null
|
||||
Invoke-Dotnet workload restore
|
||||
|
||||
# ---------------- TARGETS ----------------
|
||||
|
||||
if ($Targets -contains 'win') {
|
||||
# Windows desktop single-file exe (buildable from any OS)
|
||||
$args = @(
|
||||
'publish',
|
||||
'-c', $Configuration,
|
||||
'-r', 'win-x64',
|
||||
'--self-contained', 'true',
|
||||
'-p:PublishSingleFile=true',
|
||||
'-p:IncludeNativeLibrariesForSelfExtract=true',
|
||||
'-o', $OutWin
|
||||
)
|
||||
if ($ExplicitVersion) { $args += "-p:Version=$Version" }
|
||||
# Prevent '+<sha>' being appended to informational version in artifacts
|
||||
$args += '-p:IncludeSourceRevisionInInformationalVersion=false'
|
||||
Invoke-InProjectDir -ProjectPath $ProjDesktop @args
|
||||
}
|
||||
|
||||
if ($Targets -contains 'web') {
|
||||
# Browser/WASM (Avalonia Web)
|
||||
$args = @(
|
||||
'publish',
|
||||
'-c', $Configuration,
|
||||
'-r', 'browser-wasm',
|
||||
'--self-contained', 'true',
|
||||
'-o', $OutWeb
|
||||
)
|
||||
if ($ExplicitVersion) { $args += "-p:Version=$Version" }
|
||||
$args += '-p:IncludeSourceRevisionInInformationalVersion=false'
|
||||
Invoke-InProjectDir -ProjectPath $ProjWeb @args
|
||||
|
||||
Set-Content -Path (Join-Path $OutWeb 'README.txt') -Value @'
|
||||
Serve these files over HTTP(S). Do not open index.html via file://
|
||||
Place the folder behind any static web server.
|
||||
'@
|
||||
}
|
||||
|
||||
if ($Targets -contains 'android') {
|
||||
# Android APK (unsigned unless env vars provided)
|
||||
$baseArgs = @(
|
||||
'publish',
|
||||
'-c', $Configuration,
|
||||
'-r', 'android-arm64',
|
||||
'--self-contained', 'true',
|
||||
'-p:AndroidPackageFormat=apk',
|
||||
'-o', $OutDroid
|
||||
)
|
||||
if ($ExplicitVersion) { $baseArgs += "-p:Version=$Version" }
|
||||
$baseArgs += '-p:IncludeSourceRevisionInInformationalVersion=false'
|
||||
|
||||
# Unsigned build
|
||||
Invoke-InProjectDir -ProjectPath $ProjAndroid @baseArgs
|
||||
|
||||
# Optional signing if env vars are set
|
||||
if ($env:ANDROID_KEYSTORE -and $env:ANDROID_KEY_ALIAS -and $env:ANDROID_KEY_PASSWORD) {
|
||||
$signArgs = $baseArgs + @(
|
||||
'-p:AndroidKeyStore=true',
|
||||
"-p:AndroidSigningKeyStore=$($env:ANDROID_KEYSTORE)",
|
||||
"-p:AndroidSigningKeyAlias=$($env:ANDROID_KEY_ALIAS)",
|
||||
"-p:AndroidSigningKeyPass=$($env:ANDROID_KEY_PASSWORD)",
|
||||
"-p:AndroidSigningStorePass=$($env:ANDROID_KEY_PASSWORD)"
|
||||
)
|
||||
Invoke-InProjectDir -ProjectPath $ProjAndroid @signArgs
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`nArtifacts -> $Artifacts" -ForegroundColor Green
|
||||
Write-Host " Windows: $OutWin" -ForegroundColor Green
|
||||
Write-Host " Web: $OutWeb" -ForegroundColor Green
|
||||
Write-Host " Android: $OutDroid" -ForegroundColor Green
|
Before Width: | Height: | Size: 629 B |
5
src/global.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.403"
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AvalonEdit" version="5.0.4" targetFramework="net471" />
|
||||
<package id="csmic" version="1.1.4" targetFramework="net471" />
|
||||
</packages>
|