diff --git a/.github/workflows/Nightly.yml b/.github/workflows/Nightly.yml
index 5f900eac0..ed681a0aa 100644
--- a/.github/workflows/Nightly.yml
+++ b/.github/workflows/Nightly.yml
@@ -78,5 +78,5 @@ jobs:
# "dotnet nuget push" with "dotnet nuget add source" to GitHub Packages is unstable for project names with a dot: https://github.com/NuGet/Home/issues/9775#issuecomment-714509211
# So we must specify api-key directly in "dotnet nuget push" instead of following the GitHub Packages documentation
# --no-symbols true to not let GitHub Releases interpret .snupkg as .nupkg
- dotnet nuget push .nupkgs\*.nupkg --source 'https://nuget.pkg.github.com/verybadcat/index.json' --api-key ${{ github.token }} --skip-duplicate --no-symbols true
+ dotnet nuget push '.nupkgs\*.nupkg' --source 'https://nuget.pkg.github.com/verybadcat/index.json' --api-key ${{ github.token }} --skip-duplicate --no-symbols true
shell: pwsh
\ No newline at end of file
diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml
index 89cdb3594..d65f9c25c 100644
--- a/.github/workflows/Release.yml
+++ b/.github/workflows/Release.yml
@@ -40,5 +40,5 @@ jobs:
- name: Upload to NuGet
run: |
# Use --skip-duplicate since we want re-runs of this workflow to succeed in case of network issues
- dotnet nuget push .nupkgs\*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
+ dotnet nuget push '.nupkgs\*.nupkg' -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
shell: pwsh
diff --git a/.gitignore b/.gitignore
index 8544c14b2..59e9fdc85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,4 @@
## START Specifically added for CSharpMath
-# Special one for XamarinAndroidTemp on Mac
-C:/
-
# For CSharpMath.Rendering.Tests
CSharpMath.Rendering.Tests/*/*.*.png
@@ -12,77 +9,129 @@ CSharpMath.Xaml.Tests.NuGet/Test.*.png
/.benchmarkresults
/.nupkgs
/.testcoverage
-
## END Specifically added for CSharpMath
## 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
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
+*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
+*.env
# 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/
+
+[Dd]ebug/x64/
+[Dd]ebugPublic/x64/
+[Rr]elease/x64/
+[Rr]eleases/x64/
+bin/x64/
+obj/x64/
+
+[Dd]ebug/x86/
+[Dd]ebugPublic/x86/
+[Rr]elease/x86/
+[Rr]eleases/x86/
+bin/x86/
+obj/x86/
+
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+[Aa][Rr][Mm]64[Ee][Cc]/
bld/
-[Bb]in/
[Oo]bj/
+[Oo]ut/
[Ll]og/
+[Ll]ogs/
+
+# Build results on 'Bin' directories
+**/[Bb]in/*
+# Uncomment if you have tasks that rely on *.refresh files to move binaries
+# (https://github.com/github/gitignore/pull/3736)
+#!**/[Bb]in/*.refresh
-# Visual Studio 2015 cache/options directory
+# 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.*
+*.trx
-# NUNIT
+# NUnit
*.VisualState.xml
TestResult.xml
+nunit-*.xml
+
+# Approval Tests result files
+*.received.*
# 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/
-**/Properties/launchSettings.json
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
*_i.c
*_p.c
-*_i.h
+*_h.h
*.ilk
*.meta
*.obj
+*.idb
+*.iobj
*.pch
*.pdb
+*.ipdb
*.pgc
*.pgd
*.rsp
+# but not Directory.Build.rsp, as it configures directory-level build defaults
+!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
+*_wpftmp.csproj
*.log
+*.tlog
*.vspscc
*.vssscc
.builds
@@ -110,6 +159,9 @@ ipch/
*.vspx
*.sap
+# Visual Studio Trace Files
+*.e2e
+
# TFS 2012 Local Workspace
$tf/
@@ -121,21 +173,28 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
-# JustCode is a .NET coding add-in
-.JustCode
-
# 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_*
+.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
@@ -165,7 +224,7 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
+# 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
@@ -177,12 +236,14 @@ PublishScripts/
# NuGet Packages
*.nupkg
+# NuGet Symbol Packages
+*.snupkg
# The packages folder can be ignored because of Package Restore
-**/packages/*
+**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
-!**/packages/build/
+!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
-#!**/packages/repositories.config
+#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
@@ -200,12 +261,15 @@ 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/
+!?*.[Cc]ache/
# Others
ClientBin/
@@ -218,6 +282,10 @@ ClientBin/
*.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/
@@ -232,6 +300,8 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
# SQL Server files
*.mdf
@@ -242,6 +312,10 @@ UpgradeLog*.htm
*.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/
@@ -253,9 +327,6 @@ FakesAssemblies/
.ntvs_analysis.dat
node_modules/
-# Typescript v1 declaration files
-typings/
-
# Visual Studio 6 build log
*.plg
@@ -265,6 +336,14 @@ typings/
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -274,26 +353,25 @@ typings/
_Pvt_Extensions
# Paket dependency manager
-.paket/paket.exe
+**/.paket/paket.exe
paket-files/
# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
-.idea/
-*.sln.iml
+**/.fake/
-# CodeRush
-.cr/
+# CodeRush personal settings
+**/.cr/personal
# Python Tools for Visual Studio (PTVS)
-__pycache__/
+**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
+#tools/**
+#!tools/packages.config
+
+# Tabs Studio
+*.tss
# Telerik's JustMock configuration file
*.jmconfig
@@ -304,8 +382,60 @@ __pycache__/
*.odx.cs
*.xsd.cs
-# ignore Xamarin.Android Resource.Designer.cs files
-**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
-**/*.Android/**/[Rr]esource.[Dd]esigner.cs
-**/Android/**/[Rr]esource.[Dd]esigner.cs
-**/Droid/**/[Rr]esource.[Dd]esigner.cs
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+MSBuild_Logs/
+
+# AWS SAM Build and Temporary Artifacts folder
+.aws-sam
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+**/.mfractor/
+
+# Local History for Visual Studio
+**/.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# 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
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
\ No newline at end of file
diff --git a/CSharpMath.Maui.Example/App.xaml b/CSharpMath.Maui.Example/App.xaml
index 255f815de..b26d98216 100644
--- a/CSharpMath.Maui.Example/App.xaml
+++ b/CSharpMath.Maui.Example/App.xaml
@@ -2,6 +2,7 @@
@@ -17,6 +18,33 @@
+
+ 1
+ 2
+ 4
+ 8
+ 12
+ 16
+ 20
+ 24
+ 30
+ 36
+ 48
+ 60
+ 72
+ 96
+ 108
+ 144
+ 192
+ 288
+ 384
+ 480
+ 576
+ 666
+ 768
+ 864
+ 960
+
diff --git a/CSharpMath.Maui.Example/TextPage.xaml b/CSharpMath.Maui.Example/TextPage.xaml
index 8e4b57ba5..867b7cf33 100644
--- a/CSharpMath.Maui.Example/TextPage.xaml
+++ b/CSharpMath.Maui.Example/TextPage.xaml
@@ -5,7 +5,7 @@
xmlns:local="clr-namespace:CSharpMath.Maui.Example"
x:Class="CSharpMath.Maui.Example.TextPage"
Title="Text">
-
+
diff --git a/CSharpMath.Maui.Example/TryPage.xaml b/CSharpMath.Maui.Example/TryPage.xaml
index 28eebbce9..9debfbc16 100644
--- a/CSharpMath.Maui.Example/TryPage.xaml
+++ b/CSharpMath.Maui.Example/TryPage.xaml
@@ -5,16 +5,29 @@
xmlns:local="clr-namespace:CSharpMath.Maui.Example"
x:Class="CSharpMath.Maui.Example.TryPage"
Title="Try">
+
+
+
-
+
-
+
-
-
+
+
-
diff --git a/CSharpMath.Maui.Example/TryPage.xaml.cs b/CSharpMath.Maui.Example/TryPage.xaml.cs
index d13c7ee11..a02ab4fd7 100644
--- a/CSharpMath.Maui.Example/TryPage.xaml.cs
+++ b/CSharpMath.Maui.Example/TryPage.xaml.cs
@@ -1,19 +1,10 @@
+using System.Globalization;
+
namespace CSharpMath.Maui.Example {
public partial class TryPage : ContentPage {
- public static float[] FontSizes = new float[] {
- 1, 2, 4, 8, 12, 16, 20, 24, 30, 36, 48, 60, 72, 96, 108, 144, 192,
- 288, 384, 480, 576, 666 /*(insert trollface here)*/, 768, 864, 960
- };
public TryPage() {
InitializeComponent();
Size.SelectedItem = View.FontSize;
- Size.SelectedIndexChanged += (sender, e) => View.FontSize = (float)Size.SelectedItem;
- Entry.TextChanged += (sender, e) => {
- View.LaTeX = Entry.Text;
- if (View.ErrorMessage is { })
- Exit.FormattedText = new() { Spans = { new() { Text = View.LaTeX, TextColor = Colors.Red, FontSize = Exit.FontSize } } };
- else Exit.Text = View.LaTeX;
- };
Picker.ItemsSource = Rendering.Tests.TestRenderingMathData.AllConstants.Keys.ToList();
Picker.SelectedIndexChanged += (sender, e) => {
if (Picker.SelectedItem is string s) View.LaTeX = Entry.Text = Rendering.Tests.TestRenderingMathData.AllConstants[s];
@@ -21,11 +12,13 @@ public TryPage() {
var values = typeof(Rendering.FrontEnd.TextAlignment).GetEnumValues();
Array.Reverse(values);
Alignment.ItemsSource = values;
- Alignment.SelectedIndexChanged += (sender, e) => View.TextAlignment = (Rendering.FrontEnd.TextAlignment)Alignment.SelectedItem;
Alignment.SelectedItem = View.TextAlignment;
}
- private void Button_Clicked(object sender, System.EventArgs e) {
+ private void Button_Clicked(object sender, EventArgs e) =>
View.DisplacementX = View.DisplacementY = 0;
- }
+ }
+ public class IsNotNullConverter : IValueConverter {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value is not null;
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
diff --git a/CSharpMath.SkiaSharp/TextPainter.cs b/CSharpMath.SkiaSharp/TextPainter.cs
index b48d83ea1..e150c9d2b 100644
--- a/CSharpMath.SkiaSharp/TextPainter.cs
+++ b/CSharpMath.SkiaSharp/TextPainter.cs
@@ -4,9 +4,9 @@
namespace CSharpMath.SkiaSharp {
public class TextPainter : TextPainter {
+ public bool AntiAlias { get; set; } = true;
public override Color WrapColor(SKColor color) => color.FromNative();
public override SKColor UnwrapColor(Color color) => color.ToNative();
- public override ICanvas WrapCanvas(SKCanvas canvas) =>
- new SkiaCanvas(canvas, true);
+ public override ICanvas WrapCanvas(SKCanvas canvas) => new SkiaCanvas(canvas, AntiAlias);
}
}
diff --git a/CSharpMath.Uno.Example/.run/CSharpMath.Uno.Example.run.xml b/CSharpMath.Uno.Example/.run/CSharpMath.Uno.Example.run.xml
new file mode 100644
index 000000000..8ef29e5a6
--- /dev/null
+++ b/CSharpMath.Uno.Example/.run/CSharpMath.Uno.Example.run.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpMath.Uno.Example/.run/Readme.md b/CSharpMath.Uno.Example/.run/Readme.md
new file mode 100644
index 000000000..6e72638a0
--- /dev/null
+++ b/CSharpMath.Uno.Example/.run/Readme.md
@@ -0,0 +1,3 @@
+# About the `.run` folder
+
+This folder is present to add support for the [Rider IDE](https://aka.platform.uno/rider-getstarted). You can remove this folder safely if you're not using Rider.
diff --git a/CSharpMath.Uno.Example/.vscode/extensions.json b/CSharpMath.Uno.Example/.vscode/extensions.json
new file mode 100644
index 000000000..a63ad4009
--- /dev/null
+++ b/CSharpMath.Uno.Example/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "unoplatform.vscode"
+ ],
+}
diff --git a/CSharpMath.Uno.Example/.vscode/launch.json b/CSharpMath.Uno.Example/.vscode/launch.json
new file mode 100644
index 000000000..e7fb694ea
--- /dev/null
+++ b/CSharpMath.Uno.Example/.vscode/launch.json
@@ -0,0 +1,77 @@
+{
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Uno Platform Mobile Debug",
+ "type": "Uno",
+ "request": "launch",
+ // any Uno* task will do, this is simply to satisfy vscode requirement when a launch.json is present
+ "preLaunchTask": "Uno: android | Debug | android-x64"
+ },
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": "Uno Platform WebAssembly Debug (Chrome)",
+ "type": "chrome",
+ "request": "launch",
+ "url": "http://localhost:5000",
+ "webRoot": "${workspaceFolder}/CSharpMath.Uno.Example",
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "timeout": 30000,
+ "preLaunchTask": "build-wasm",
+ "server": {
+ "runtimeExecutable": "dotnet",
+ "program": "run",
+ "args": ["--no-build","-f","net10.0-browserwasm","--launch-profile", "CSharpMath.Uno.Example (WebAssembly)"],
+ "outputCapture": "std",
+ "timeout": 30000,
+ "cwd": "${workspaceFolder}/CSharpMath.Uno.Example"
+ }
+ },
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": "Uno Platform WebAssembly Debug (Edge)",
+ "type": "msedge",
+ "request": "launch",
+ "url": "http://localhost:5000",
+ "webRoot": "${workspaceFolder}/CSharpMath.Uno.Example",
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "timeout": 30000,
+ "preLaunchTask": "build-wasm",
+ "server": {
+ "runtimeExecutable": "dotnet",
+ "program": "run",
+ "args": ["--no-build","-f","net10.0-browserwasm","--launch-profile", "CSharpMath.Uno.Example (WebAssembly)"],
+ "outputCapture": "std",
+ "timeout": 30000,
+ "cwd": "${workspaceFolder}/CSharpMath.Uno.Example"
+ }
+ },
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": "Uno Platform Desktop Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build-desktop",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/CSharpMath.Uno.Example/bin/Debug/net10.0-desktop/CSharpMath.Uno.Example.dll",
+ "args": [],
+ "launchSettingsProfile": "CSharpMath.Uno.Example (Desktop)",
+ "env": {
+ "DOTNET_MODIFIABLE_ASSEMBLIES": "debug"
+ },
+ "cwd": "${workspaceFolder}/CSharpMath.Uno.Example",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "internalConsole",
+ "stopAtEntry": false
+ },
+ ]
+}
diff --git a/CSharpMath.Uno.Example/.vscode/settings.json b/CSharpMath.Uno.Example/.vscode/settings.json
new file mode 100644
index 000000000..3405922d5
--- /dev/null
+++ b/CSharpMath.Uno.Example/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.expand": false,
+ "explorer.fileNesting.patterns": {
+ "*.xaml": "$(capture).xaml.cs"
+ },
+ "files.associations": {
+ "global.json": "jsonc"
+ }
+}
diff --git a/CSharpMath.Uno.Example/.vscode/tasks.json b/CSharpMath.Uno.Example/.vscode/tasks.json
new file mode 100644
index 000000000..7f6845ee2
--- /dev/null
+++ b/CSharpMath.Uno.Example/.vscode/tasks.json
@@ -0,0 +1,57 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build-wasm",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net10.0-browserwasm",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish-wasm",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net10.0-browserwasm",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "build-desktop",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net10.0-desktop",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish-desktop",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:TargetFramework=net10.0-desktop",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
diff --git a/CSharpMath.Uno.Example/App.xaml b/CSharpMath.Uno.Example/App.xaml
new file mode 100644
index 000000000..8ee4e8d68
--- /dev/null
+++ b/CSharpMath.Uno.Example/App.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpMath.Uno.Example/App.xaml.cs b/CSharpMath.Uno.Example/App.xaml.cs
new file mode 100644
index 000000000..f197edc28
--- /dev/null
+++ b/CSharpMath.Uno.Example/App.xaml.cs
@@ -0,0 +1,133 @@
+using System;
+using Microsoft.Extensions.Logging;
+using Uno.Resizetizer;
+
+namespace CSharpMath.Uno.Example;
+
+public partial class App : Application
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected Window? MainWindow { get; private set; }
+
+ protected override void OnLaunched(LaunchActivatedEventArgs args)
+ {
+ MainWindow = new Window();
+#if DEBUG
+ MainWindow.UseStudio();
+#endif
+
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (MainWindow.Content is not Frame rootFrame)
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ // Place the frame in the current Window
+ MainWindow.Content = rootFrame;
+
+ rootFrame.NavigationFailed += OnNavigationFailed;
+ }
+
+ if (rootFrame.Content == null)
+ {
+ // When the navigation stack isn't restored navigate to the first page,
+ // configuring the new page by passing required information as a navigation
+ // parameter
+ rootFrame.Navigate(typeof(MainPage), args.Arguments);
+ }
+
+ MainWindow.SetWindowIcon();
+ // Ensure the current window is active
+ MainWindow.Activate();
+ }
+
+ ///
+ /// Invoked when Navigation to a certain page fails
+ ///
+ /// The Frame which failed navigation
+ /// Details about the navigation failure
+ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new InvalidOperationException($"Failed to load {e.SourcePageType.FullName}: {e.Exception}");
+ }
+
+ ///
+ /// Configures global Uno Platform logging
+ ///
+ public static void InitializeLogging()
+ {
+#if DEBUG
+ // Logging is disabled by default for release builds, as it incurs a significant
+ // initialization cost from Microsoft.Extensions.Logging setup. If startup performance
+ // is a concern for your application, keep this disabled. If you're running on the web or
+ // desktop targets, you can use URL or command line parameters to enable it.
+ //
+ // For more performance documentation: https://platform.uno/docs/articles/Uno-UI-Performance.html
+
+ var factory = LoggerFactory.Create(builder =>
+ {
+#if __WASM__
+ builder.AddProvider(new global::Uno.Extensions.Logging.WebAssembly.WebAssemblyConsoleLoggerProvider());
+#elif __IOS__
+ builder.AddProvider(new global::Uno.Extensions.Logging.OSLogLoggerProvider());
+
+ // Log to the Visual Studio Debug console
+ builder.AddConsole();
+#else
+ builder.AddConsole();
+#endif
+
+ // Exclude logs below this level
+ builder.SetMinimumLevel(LogLevel.Information);
+
+ // Default filters for Uno Platform namespaces
+ builder.AddFilter("Uno", LogLevel.Warning);
+ builder.AddFilter("Windows", LogLevel.Warning);
+ builder.AddFilter("Microsoft", LogLevel.Warning);
+
+ // Generic Xaml events
+ // builder.AddFilter("Microsoft.UI.Xaml", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.VisualStateGroup", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.StateTriggerBase", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.UIElement", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.FrameworkElement", LogLevel.Trace );
+
+ // Layouter specific messages
+ // builder.AddFilter("Microsoft.UI.Xaml.Controls", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.Controls.Layouter", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.Controls.Panel", LogLevel.Debug );
+
+ // builder.AddFilter("Windows.Storage", LogLevel.Debug );
+
+ // Binding related messages
+ // builder.AddFilter("Microsoft.UI.Xaml.Data", LogLevel.Debug );
+ // builder.AddFilter("Microsoft.UI.Xaml.Data", LogLevel.Debug );
+
+ // Binder memory references tracking
+ // builder.AddFilter("Uno.UI.DataBinding.BinderReferenceHolder", LogLevel.Debug );
+
+ // DevServer and HotReload related
+ // builder.AddFilter("Uno.UI.RemoteControl", LogLevel.Information);
+
+ // Debug JS interop
+ // builder.AddFilter("Uno.Foundation.WebAssemblyRuntime", LogLevel.Debug );
+ });
+
+ global::Uno.Extensions.LogExtensionPoint.AmbientLoggerFactory = factory;
+
+#if HAS_UNO
+ global::Uno.UI.Adapter.Microsoft.Extensions.Logging.LoggingAdapter.Initialize();
+#endif
+#endif
+ }
+}
diff --git a/CSharpMath.Uno.Example/Assets/Icons/icon.svg b/CSharpMath.Uno.Example/Assets/Icons/icon.svg
new file mode 100644
index 000000000..a15af53aa
--- /dev/null
+++ b/CSharpMath.Uno.Example/Assets/Icons/icon.svg
@@ -0,0 +1,42 @@
+
+
diff --git a/CSharpMath.Uno.Example/Assets/Icons/icon_foreground.svg b/CSharpMath.Uno.Example/Assets/Icons/icon_foreground.svg
new file mode 100644
index 000000000..8ffc41ae3
--- /dev/null
+++ b/CSharpMath.Uno.Example/Assets/Icons/icon_foreground.svg
@@ -0,0 +1,137 @@
+
+
diff --git a/CSharpMath.Uno.Example/Assets/SharedAssets.md b/CSharpMath.Uno.Example/Assets/SharedAssets.md
new file mode 100644
index 000000000..b1cc4e762
--- /dev/null
+++ b/CSharpMath.Uno.Example/Assets/SharedAssets.md
@@ -0,0 +1,32 @@
+# Shared Assets
+
+See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md
+
+## Here is a cheat sheet
+
+1. Add the image file to the `Assets` directory of a shared project.
+2. Set the build action to `Content`.
+3. (Recommended) Provide an asset for various scales/dpi
+
+### Examples
+
+```text
+\Assets\Images\logo.scale-100.png
+\Assets\Images\logo.scale-200.png
+\Assets\Images\logo.scale-400.png
+
+\Assets\Images\scale-100\logo.png
+\Assets\Images\scale-200\logo.png
+\Assets\Images\scale-400\logo.png
+```
+
+### Table of scales
+
+| Scale | WinUI | iOS | Android |
+|-------|:-----------:|:---------------:|:-------:|
+| `100` | scale-100 | @1x | mdpi |
+| `125` | scale-125 | N/A | N/A |
+| `150` | scale-150 | N/A | hdpi |
+| `200` | scale-200 | @2x | xhdpi |
+| `300` | scale-300 | @3x | xxhdpi |
+| `400` | scale-400 | N/A | xxxhdpi |
diff --git a/CSharpMath.Uno.Example/Assets/Splash/splash_screen.svg b/CSharpMath.Uno.Example/Assets/Splash/splash_screen.svg
new file mode 100644
index 000000000..8ffc41ae3
--- /dev/null
+++ b/CSharpMath.Uno.Example/Assets/Splash/splash_screen.svg
@@ -0,0 +1,137 @@
+
+
diff --git a/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj b/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj
new file mode 100644
index 000000000..fe868486f
--- /dev/null
+++ b/CSharpMath.Uno.Example/CSharpMath.Uno.Example.csproj
@@ -0,0 +1,33 @@
+
+
+ net10.0-android;net10.0-browserwasm;net10.0-desktop
+ $(TargetFrameworks);net10.0-ios
+ $(TargetFrameworks);net10.0-windows10.0.19041.0
+
+ Exe
+ true
+ enable
+
+
+ CSharpMath.Uno.Example
+
+ com.companyname.csharpmath-uno-example
+
+ 1.0
+ 1
+
+ CSharpMath.Uno.Example
+
+ CSharpMath.Uno.Example powered by Uno Platform.
+
+
+ SkiaRenderer
+
+
+
+
+
+
diff --git a/CSharpMath.Uno.Example/MainPage.xaml b/CSharpMath.Uno.Example/MainPage.xaml
new file mode 100644
index 000000000..e809131e0
--- /dev/null
+++ b/CSharpMath.Uno.Example/MainPage.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpMath.Uno.Example/MainPage.xaml.cs b/CSharpMath.Uno.Example/MainPage.xaml.cs
new file mode 100644
index 000000000..2fc9db9f5
--- /dev/null
+++ b/CSharpMath.Uno.Example/MainPage.xaml.cs
@@ -0,0 +1,9 @@
+namespace CSharpMath.Uno.Example;
+
+public sealed partial class MainPage : Page
+{
+ public MainPage()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/CSharpMath.Uno.Example/Package.appxmanifest b/CSharpMath.Uno.Example/Package.appxmanifest
new file mode 100644
index 000000000..9ef381467
--- /dev/null
+++ b/CSharpMath.Uno.Example/Package.appxmanifest
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSharpMath.Uno.Example/Pages/ClockPage.xaml b/CSharpMath.Uno.Example/Pages/ClockPage.xaml
new file mode 100644
index 000000000..96b20c57b
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/ClockPage.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpMath.Uno.Example/Pages/ClockPage.xaml.cs b/CSharpMath.Uno.Example/Pages/ClockPage.xaml.cs
new file mode 100644
index 000000000..161aacab3
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/ClockPage.xaml.cs
@@ -0,0 +1,113 @@
+using CSharpMath.SkiaSharp;
+using SkiaSharp;
+using SkiaSharp.Views.Windows;
+using static System.Math;
+
+namespace CSharpMath.Uno.Example {
+ public sealed partial class ClockPage : Page {
+ private readonly DispatcherTimer _timer;
+
+ public ClockPage() {
+ this.InitializeComponent();
+ _timer = new DispatcherTimer();
+ _timer.Interval = TimeSpan.FromMilliseconds(200);
+ _timer.Tick += (s, e) => {
+ canvasView.Invalidate();
+ };
+ this.Loaded += ClockPage_Loaded;
+ this.Unloaded += ClockPage_Unloaded;
+ }
+
+ private void ClockPage_Loaded(object sender, RoutedEventArgs e) {
+ _timer.Start();
+ }
+
+ private void ClockPage_Unloaded(object sender, RoutedEventArgs e) {
+ _timer.Stop();
+ }
+
+ readonly SKPaint blackFillPaint = new SKPaint {
+ Style = SKPaintStyle.Fill,
+ Color = SKColors.Black
+ };
+ readonly SKPaint whiteFillPaint = new SKPaint {
+ Style = SKPaintStyle.Fill,
+ Color = SKColors.White
+ };
+ readonly SKPaint whiteStrokePaint = new SKPaint {
+ Style = SKPaintStyle.Stroke,
+ Color = SKColors.White,
+ StrokeCap = SKStrokeCap.Round,
+ IsAntialias = true
+ };
+ readonly SKPaint redStrokePaint = new SKPaint {
+ Style = SKPaintStyle.Stroke,
+ Color = SKColors.Red,
+ StrokeCap = SKStrokeCap.Round,
+ IsAntialias = true
+ };
+ readonly string[] labels = {
+ // Four 4s make 1 to 12 using different operations
+ @"$\frac{44+4}{4}$",
+ @"$\frac{44}{44}$",
+ @"$\frac{4}{4}+\frac{4}{4}$",
+ @"$\frac{4+4+4}{4}$",
+ @"$4+\frac{4-4}{4}$",
+ @"$4+4^{4-4}$",
+ @"$4+\frac{4+4}{4}$",
+ @"$\frac{44}{4}-4$",
+ @"$\sqrt{4}^{4-\frac{4}{4}}$",
+ @"$\:\:(4-\frac{4}{4})^{\sqrt{4}}$",
+ @"$\frac{44-4}{4}$",
+ @"$\frac{4!}{\sqrt{4}}-\frac{4}{4}$"
+ };
+ private void CanvasView_PaintSurface(object sender, SKPaintSurfaceEventArgs e) {
+ var canvas = e.Surface.Canvas;
+ canvas.Clear(SKColors.CornflowerBlue);
+ canvas.Translate(e.Info.Width / 2, e.Info.Height / 2);
+ var minWidthHeight = Min(e.Info.Width, e.Info.Height);
+ canvas.Scale(minWidthHeight / 210f);
+ canvas.DrawCircle(0, 0, 100, blackFillPaint);
+ var painter = new TextPainter { FontSize = 8, TextColor = SKColors.White };
+ for (int i = 0; i < 60; i++) {
+ // Dots
+ canvas.Save();
+ canvas.RotateDegrees(6 * i);
+ canvas.DrawCircle(0, -90, i % 5 == 0 ? 4 : 2, whiteFillPaint);
+ canvas.Restore();
+ // Maths
+ if (i % 5 == 0) {
+ painter.LaTeX = labels[i / 5];
+ if (!(painter.Measure(e.Info.Width) is { } measure))
+ throw new Atom.InvalidCodePathException("Invalid LaTeX");
+ var θ = (90 - 6 * i) / 180f * PI;
+ var sinθ = (float)Sin(θ);
+ var cosθ = (float)Cos(θ);
+ painter.Draw(canvas,
+ new System.Drawing.PointF(75 * cosθ - (float)measure.Width / 2,
+ -75 * sinθ - (float)measure.Height / 2),
+ float.PositiveInfinity);
+ }
+ }
+ var dateTime = DateTime.Now;
+ // H
+ canvas.Save();
+ canvas.RotateDegrees(30 * dateTime.Hour + dateTime.Minute / 2f);
+ whiteStrokePaint.StrokeWidth = 12;
+ canvas.DrawLine(0, 0, 0, -50, whiteStrokePaint);
+ canvas.Restore();
+ // M
+ canvas.Save();
+ canvas.RotateDegrees(6 * dateTime.Minute + dateTime.Second / 10f);
+ whiteStrokePaint.StrokeWidth = 6;
+ canvas.DrawLine(0, 0, 0, -65, whiteStrokePaint);
+ canvas.Restore();
+ // S
+ canvas.Save();
+ canvas.RotateDegrees(6f * (dateTime.Second + dateTime.Millisecond / 1000f));
+ redStrokePaint.StrokeWidth = 2;
+ canvas.DrawLine(0, 0, 0, -75, redStrokePaint);
+ canvas.Restore();
+ }
+ }
+}
\ No newline at end of file
diff --git a/CSharpMath.Uno.Example/Pages/TextPage.xaml b/CSharpMath.Uno.Example/Pages/TextPage.xaml
new file mode 100644
index 000000000..39f9d917e
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/TextPage.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpMath.Uno.Example/Pages/TextPage.xaml.cs b/CSharpMath.Uno.Example/Pages/TextPage.xaml.cs
new file mode 100644
index 000000000..606b5c317
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/TextPage.xaml.cs
@@ -0,0 +1,11 @@
+namespace CSharpMath.Uno.Example;
+
+using Microsoft.UI;
+using Microsoft.UI.Xaml.Media;
+
+public sealed partial class TextPage : Page {
+ public static float[] FontSizes { get; } = TryPage.FontSizes;
+ public TextPage() {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/CSharpMath.Uno.Example/Pages/TryPage.xaml b/CSharpMath.Uno.Example/Pages/TryPage.xaml
new file mode 100644
index 000000000..dc4aa5709
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/TryPage.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CSharpMath.Uno.Example/Pages/TryPage.xaml.cs b/CSharpMath.Uno.Example/Pages/TryPage.xaml.cs
new file mode 100644
index 000000000..c074e600b
--- /dev/null
+++ b/CSharpMath.Uno.Example/Pages/TryPage.xaml.cs
@@ -0,0 +1,77 @@
+namespace CSharpMath.Uno.Example;
+
+using Microsoft.UI;
+using Microsoft.UI.Xaml.Media;
+
+public sealed partial class TryPage : Page {
+ public static float[] FontSizes { get; } = [
+ 1, 2, 4, 8, 12, 16, 20, 24, 30, 36, 48, 60, 72, 96, 108, 144, 192,
+ 288, 384, 480, 576, 666, 768, 864, 960
+ ];
+
+ public Rendering.FrontEnd.TextAlignment[] AlignmentValues { get; }
+
+ public TryPage() {
+ var values = (Rendering.FrontEnd.TextAlignment[])typeof(Rendering.FrontEnd.TextAlignment).GetEnumValues();
+ System.Array.Reverse(values);
+ AlignmentValues = values;
+ InitializeComponent();
+ Alignment.SelectedItem = View.TextAlignment;
+ View.RegisterPropertyChangedCallback(MathView.ErrorMessageProperty, (sender, dp) => {
+ Exit.Foreground = View.ErrorMessage is not null
+ ? new SolidColorBrush(Colors.Red)
+ : (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"];
+ });
+ }
+
+ private void ResetPan_Clicked(object sender, RoutedEventArgs e) {
+ View.DisplacementX = View.DisplacementY = 0;
+ }
+
+ private async void Calculate_Clicked(object sender, RoutedEventArgs e) {
+ if (View.Painter.Content is not { } content) return;
+ var popupView = new MathView {
+ FontSize = 32,
+ EnablePanning = true,
+ TextAlignment = Rendering.FrontEnd.TextAlignment.TopLeft,
+ LaTeX = CSharpMath.Evaluation.Interpret(content),
+ HorizontalAlignment = HorizontalAlignment.Stretch,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ TextColor = View.TextColor,
+ Width = 400,
+ Height = 300
+ };
+ static Button Tap(Button button, Action