Compare commits

...

3 Commits

Author SHA1 Message Date
b7a9f0cfc2 changed readme 2026-01-11 06:16:11 +05:00
dd3cff0eaa AutoGrid and build.sh 2026-01-11 06:02:25 +05:00
a08838bb8b made buttons square and added hotkeys 2026-01-11 04:27:37 +05:00
12 changed files with 176 additions and 108 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "FusionCalculator"] [submodule "FusionCalculator"]
path = FusionCalculator path = FusionCalculator
url = https://timerix.ddns.net:3322/Timerix/FusionCalculator.git url = https://timerix.ddns.net/git/Timerix/FusionCalculator.git

View File

@@ -2,12 +2,20 @@
A calculator application written in C# and Fusion. A calculator application written in C# and Fusion.
Using Avalonia to draw GUI. Using Avalonia to draw GUI.
You can just type expression if tou don't like pressing GUI buttons.
**Key binds:**
- `Enter`: `=`
- `Del`: `AC`
![screenshot](./screenshot.jpg)
## Building ## Building
Requirements: git, bash, [dotnet8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0), [fut](https://github.com/fusionlanguage/fut) Requirements: git, bash, [dotnet8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0), [fut](https://github.com/fusionlanguage/fut)
1. ensure that [FusionCalculator](https://github.com/Timerix22/FusionCalculator) submodule is up-to-date. 1. ensure that [FusionCalculator](https://github.com/Timerix22/FusionCalculator) submodule is up-to-date.
```shell ```shell
git submodule init && git submodule update it submodule update --init
``` ```
2. Translate FusionCalculator to C# 2. Translate FusionCalculator to C#
```shell ```shell

View File

@@ -11,14 +11,14 @@
<Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/>
</Style> </Style>
<Style Selector="Button.cirlce"> <Style Selector="Button.button">
<Setter Property="Height" Value="60"/> <Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Width" Value="60"/> <Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="CornerRadius" Value="30"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Margin" Value="1"/>
<Setter Property="Padding" Value="0"/>
</Style> </Style>
<Style Selector="Button.gray"> <Style Selector="Button.gray">

View File

@@ -4,7 +4,6 @@ global using System.Globalization;
global using Avalonia; global using Avalonia;
global using Avalonia.Controls; global using Avalonia.Controls;
global using Avalonia.Interactivity; global using Avalonia.Interactivity;
global using Avalonia.Media;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;

View File

@@ -1,162 +1,176 @@
<Window <Window
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ag="clr-namespace:AvaloniaAutoGrid;assembly=AvaloniaAutoGrid"
x:Class="SharpCalculator.Avalonia.MainWindow" x:Class="SharpCalculator.Avalonia.MainWindow"
Title="SharpCalculator" Title="SharpCalculator"
Icon="avares://SharpCalculator.Avalonia/assets/icon.ico" Icon="avares://SharpCalculator.Avalonia/assets/icon.ico"
FontFamily="Consolas" FontSize="24" FontFamily="Consolas" FontSize="24"
MinWidth="460" MinHeight="570"
Width="460" Height="570"> Width="460" Height="570">
<Grid Margin="10"> <!-- ReSharper disable Xaml.MissingGridIndex -->
<Grid.RowDefinitions>1* 40 1* 330</Grid.RowDefinitions> <ag:AutoGrid Margin="10"
ColumnDefinitions="*"
<TextBox Name="Input" Grid.Row="0" RowDefinitions="1* 40 1* 330">
<TextBox Name="Input"
TextChanged="Input_OnTextChanged" TextChanged="Input_OnTextChanged"
BorderThickness="2" BorderThickness="2"
BorderBrush="#AAAAAA" BorderBrush="#AAAAAA"
TextWrapping="Wrap"/> TextWrapping="Wrap"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<TextBlock Grid.Row="1" <StackPanel Orientation="Horizontal">
VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Foreground="#AAAAAA" Foreground="#AAAAAA"
Text="="/> Text="="/>
<TextBox Name="Output" Grid.Row="1" <TextBox Name="Output"
Margin="16 0 0 0" Margin="16 0 0 0"
VerticalAlignment="Center" VerticalAlignment="Center"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
IsReadOnly="True" IsReadOnly="True"
BorderThickness="0" BorderThickness="0"
TextWrapping="NoWrap"/> TextWrapping="NoWrap"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"/>
</StackPanel>
<TextBox Name="History" Grid.Row="2" <TextBox Name="History"
Foreground="Gray" Foreground="Gray"
BorderThickness="1" BorderThickness="1"
BorderBrush="Gray" BorderBrush="Gray"
IsReadOnly="True" IsReadOnly="True"
TextWrapping="NoWrap" TextWrapping="NoWrap"
FontSize="18"/> FontSize="18"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<Grid Grid.Row="3" VerticalAlignment="Bottom" <!-- ReSharper disable once Xaml.MissingGridIndex -->
MinWidth="440" MaxWidth="440" MinHeight="320" MaxHeight="320"> <ag:AutoGrid
<Grid.ColumnDefinitions>* * * * * * *</Grid.ColumnDefinitions> ColumnDefinitions="*"
<Grid.RowDefinitions>* * * * *</Grid.RowDefinitions> RowDefinitions="*"
ColumnCount="7" RowCount="5"
VerticalAlignment="Bottom"
MinWidth="440" MaxWidth="440"
MinHeight="320" MaxHeight="320">
<!-- 1st row --> <!-- 1st row -->
<Button Classes="cirlce gray" Grid.Column="0" Grid.Row="0" <Button Classes="button gray"
Click="ClearButton_OnClick" Content="AC"/> Click="ClearButton_OnClick" Content="AC"/>
<Button Classes="cirlce gray" Grid.Column="1" Grid.Row="0" <Button Classes="button gray"
Click="MathButton_OnClick" Content="("/> Click="MathButton_OnClick" Content="("/>
<Button Classes="cirlce gray" Grid.Column="2" Grid.Row="0" <Button Classes="button gray"
Click="MathButton_OnClick" Content=")" /> Click="MathButton_OnClick" Content=")" />
<Button Classes="cirlce orange" Grid.Column="3" Grid.Row="0" <Button Classes="button orange"
Click="MathButton_OnClick" Content="/"/> Click="MathButton_OnClick" Content="/"/>
<Button Classes="cirlce gray" Grid.Column="4" Grid.Row="0" <Button Classes="button gray"
Click="MathButton_OnClick" Content="1/x"/> Click="MathButton_OnClick" Content="1/x"/>
<Button Classes="cirlce gray" Grid.Column="5" Grid.Row="0" <Button Classes="button gray"
Click="MathButton_OnClick" Content="sin"/> Click="MathButton_OnClick" Content="sin"/>
<Button Classes="cirlce gray" Grid.Column="6" Grid.Row="0" <Button Classes="button gray"
Click="MathButton_OnClick" Content="asin"/> Click="MathButton_OnClick" Content="asin"/>
<!-- 2nd row --> <!-- 2nd row -->
<Button Classes="cirlce" Grid.Column="0" Grid.Row="1" <Button Classes="button"
Click="MathButton_OnClick" Content="7"/> Click="MathButton_OnClick" Content="7"/>
<Button Classes="cirlce" Grid.Column="1" Grid.Row="1" <Button Classes="button"
Click="MathButton_OnClick" Content="8"/> Click="MathButton_OnClick" Content="8"/>
<Button Classes="cirlce" Grid.Column="2" Grid.Row="1" <Button Classes="button"
Click="MathButton_OnClick" Content="9"/> Click="MathButton_OnClick" Content="9"/>
<Button Classes="cirlce orange" Grid.Column="3" Grid.Row="1" <Button Classes="button orange"
Click="MathButton_OnClick" Content="*"/> Click="MathButton_OnClick" Content="*"/>
<Button Classes="cirlce gray" Grid.Column="4" Grid.Row="1" <Button Classes="button gray"
Click="MathButton_OnClick" Content="^"/> Click="MathButton_OnClick" Content="^"/>
<Button Classes="cirlce gray" Grid.Column="5" Grid.Row="1" <Button Classes="button gray"
Click="MathButton_OnClick" Content="cos"/> Click="MathButton_OnClick" Content="cos"/>
<Button Classes="cirlce gray" Grid.Column="6" Grid.Row="1" <Button Classes="button gray"
Click="MathButton_OnClick" Content="acos"/> Click="MathButton_OnClick" Content="acos"/>
<!-- 3th row --> <!-- 3th row -->
<Button Classes="cirlce" Grid.Column="0" Grid.Row="2" <Button Classes="button"
Click="MathButton_OnClick" Content="4"/> Click="MathButton_OnClick" Content="4"/>
<Button Classes="cirlce" Grid.Column="1" Grid.Row="2" <Button Classes="button"
Click="MathButton_OnClick" Content="5"/> Click="MathButton_OnClick" Content="5"/>
<Button Classes="cirlce" Grid.Column="2" Grid.Row="2" <Button Classes="button"
Click="MathButton_OnClick" Content="6"/> Click="MathButton_OnClick" Content="6"/>
<Button Classes="cirlce orange" Grid.Column="3" Grid.Row="2" <Button Classes="button orange"
Click="MathButton_OnClick" Content="-"/> Click="MathButton_OnClick" Content="-"/>
<Button Classes="cirlce gray" Grid.Column="4" Grid.Row="2" <Button Classes="button gray"
FontSize="22" FontSize="22"
Click="MathButton_OnClick" Content="10^x"/> Click="MathButton_OnClick" Content="10^x"/>
<Button Classes="cirlce gray" Grid.Column="5" Grid.Row="2" <Button Classes="button gray"
Click="MathButton_OnClick" Content="tg"/> Click="MathButton_OnClick" Content="tg"/>
<Button Classes="cirlce gray" Grid.Column="6" Grid.Row="2" <Button Classes="button gray"
Click="MathButton_OnClick" Content="atg"/> Click="MathButton_OnClick" Content="atg"/>
<!-- 4th row --> <!-- 4th row -->
<Button Classes="cirlce" Grid.Column="0" Grid.Row="3" <Button Classes="button"
Click="MathButton_OnClick" Content="1"/> Click="MathButton_OnClick" Content="1"/>
<Button Classes="cirlce" Grid.Column="1" Grid.Row="3" <Button Classes="button"
Click="MathButton_OnClick" Content="2"/> Click="MathButton_OnClick" Content="2"/>
<Button Classes="cirlce" Grid.Column="2" Grid.Row="3" <Button Classes="button"
Click="MathButton_OnClick" Content="3"/> Click="MathButton_OnClick" Content="3"/>
<Button Classes="cirlce orange" Grid.Column="3" Grid.Row="3" <Button Classes="button orange"
Click="MathButton_OnClick" Content="+"/> Click="MathButton_OnClick" Content="+"/>
<Button Classes="cirlce gray" Grid.Column="4" Grid.Row="3" <Button Classes="button gray"
Click="MathButton_OnClick" Content="e"/> Click="MathButton_OnClick" Content="e"/>
<Button Classes="cirlce gray" Grid.Column="5" Grid.Row="3" <Button Classes="button gray"
Click="MathButton_OnClick" Content="ctg"/> Click="MathButton_OnClick" Content="ctg"/>
<Button Classes="cirlce gray" Grid.Column="6" Grid.Row="3" <Button Classes="button gray"
Click="MathButton_OnClick" Content="actg"/> Click="MathButton_OnClick" Content="actg"/>
<!-- 5th row --> <!-- 5th row -->
<Button Classes="cirlce" Grid.Column="0" Grid.Row="4" <Button Classes="button"
Click="MathButton_OnClick" Content="0"/> Click="MathButton_OnClick" Content="0"/>
<Button Classes="cirlce" Grid.Column="1" Grid.Row="4" <Button Classes="button"
Click="MathButton_OnClick" Content="."/> Click="MathButton_OnClick" Content="."/>
<Button Classes="cirlce" Grid.Column="2" Grid.Row="4" <Button Classes="button"
Click="MathButton_OnClick" Content="%"/> Click="MathButton_OnClick" Content="%"/>
<Button Classes="cirlce orange" Grid.Column="3" Grid.Row="4" <Button Classes="button orange"
Click="ResultButton_OnClick" Content="="/> Click="ResultButton_OnClick" Content="="/>
<Button Classes="cirlce gray" Grid.Column="4" Grid.Row="4" <Button Classes="button gray"
Click="MathButton_OnClick" Content="π"/> Click="MathButton_OnClick" Content="π"/>
<Button Classes="cirlce gray" Grid.Column="5" Grid.Row="4" <Button Classes="button gray"
FontSize="22" FontSize="22"
Click="MathButton_OnClick" Content="rand"/> Click="MathButton_OnClick" Content="rand"/>
<Button Classes="cirlce gray" Grid.Column="6" Grid.Row="4" <Button Classes="button gray"
Click="MathButton_OnClick" Content="ln"/> Click="MathButton_OnClick" Content="ln"/>
</Grid> </ag:AutoGrid>
</Grid> </ag:AutoGrid>
</Window> </Window>

View File

@@ -1,3 +1,4 @@
using Avalonia.Input;
using FusionCalculator; using FusionCalculator;
namespace SharpCalculator.Avalonia; namespace SharpCalculator.Avalonia;
@@ -9,6 +10,31 @@ public partial class MainWindow : Window
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
// https://github.com/AvaloniaUI/Avalonia/discussions/12179
KeyDownEvent.AddClassHandler<TopLevel>(GlobalOnKeyDown, handledEventsToo: true);
}
private void GlobalOnKeyDown(object? sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Enter:
CalculateResult();
break;
case Key.Delete:
Clear();
break;
}
}
private void CalculateResult()
{
if(Input.Text == null || Output.Text == null || Output.Text.StartsWith("Error"))
return;
string expression = Input.Text;
string result = Output.Text;
AppendToHistory($"{expression}={result}");
Input.Text = result;
} }
private void AppendToHistory(string line) //function of appending expression to the history list private void AppendToHistory(string line) //function of appending expression to the history list
@@ -24,6 +50,23 @@ public partial class MainWindow : Window
.Replace(" ", " "); .Replace(" ", " ");
} }
private void Clear()
{
Input.Clear();
Output.Clear();
}
private void ResultButton_OnClick(object? sender, RoutedEventArgs e)
{
CalculateResult();
}
private void ClearButton_OnClick(object? sender, RoutedEventArgs e)
{
Clear();
}
private void MathButton_OnClick(object? sender, RoutedEventArgs e) private void MathButton_OnClick(object? sender, RoutedEventArgs e)
{ {
if (sender is not Button button) if (sender is not Button button)
@@ -49,30 +92,6 @@ public partial class MainWindow : Window
}; };
} }
private void ClearButton_OnClick(object? sender, RoutedEventArgs e)
{
Input.Text = "";
Output.Text = "";
}
private void UndoButton_OnClick(object? sender, RoutedEventArgs e)
{
// delete last digit
// if (!string.IsNullOrEmpty(Input.Text))
// Input.Text = Input.Text.Remove(Input.Text.Length - 1);
Input.Undo();
}
private void ResultButton_OnClick(object? sender, RoutedEventArgs e)
{
if(Input.Text == null || Output.Text == null || Output.Text.StartsWith("Error"))
return;
string expression = Input.Text;
string result = Output.Text;
AppendToHistory($"{expression}={result}");
Input.Text = result;
}
private void Input_OnTextChanged(object? sender, TextChangedEventArgs e) private void Input_OnTextChanged(object? sender, TextChangedEventArgs e)
{ {
if(Input.Text == null) if(Input.Text == null)

View File

@@ -1,8 +1,4 @@
using Avalonia; namespace SharpCalculator.Avalonia;
using System;
using System.Globalization;
namespace SharpCalculator.Avalonia;
class Program class Program
{ {

View File

@@ -1,20 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<LangVersion>12</LangVersion> <LangVersion>latest</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault> <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>assets\icon.ico</ApplicationIcon> <ApplicationIcon>assets\icon.ico</ApplicationIcon>
<PublishTrimmed>true</PublishTrimmed>
<PublishAot>true</PublishAot>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.6" /> <PackageReference Include="Avalonia" Version="11.3.10" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.6" /> <PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
<PackageReference Include="Avalonia.Themes.Simple" Version="11.0.6" /> <PackageReference Include="Avalonia.Themes.Simple" Version="11.3.10" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.6" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.10" />
<PackageReference Include="Russkyc.AvaloniaAutoGrid" Version="1.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@@ -9,13 +9,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.6.4" /> <PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6"> <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.0"> <PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>

View File

@@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution_items", "solution_
.gitignore = .gitignore .gitignore = .gitignore
.gitmodules = .gitmodules .gitmodules = .gitmodules
README.md = README.md README.md = README.md
build.sh = build.sh
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCalculator.Tests", "SharpCalculator.Tests\SharpCalculator.Tests.csproj", "{E929E48D-D11B-4FB5-AC6F-D0D50E2B3BD1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCalculator.Tests", "SharpCalculator.Tests\SharpCalculator.Tests.csproj", "{E929E48D-D11B-4FB5-AC6F-D0D50E2B3BD1}"

28
build.sh Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -eo pipefail
outdir="bin"
project="SharpCalculator.Avalonia"
# Somebody broke dotnet10 sdk .target files
# To publish self-contained trimmed binary you should add <PublishTrimmed>true</PublishTrimmed> to project file.
# Command-line option -p:PublishTrimmed=true produces an error.
# Same with <PublishAot>true</PublishAot> if you want AOT compilation.
args="
--self-contained
--use-current-runtime
-p:PublishSingleFile=true
-p:TrimMode=partial
-p:EnableCompressionInSingleFile=true
-p:OptimizationPreference=Size
-p:InvariantGlobalization=true
-p:DebugType=none
-p:IncludeNativeLibrariesForSelfExtract=true"
rm -rf "$outdir"
command="dotnet publish $project -c Release -o $outdir $args"
echo "$command"
$command
find "$outdir" -name '*.pdb' -delete -printf "deleted '%p'\n"
tree -sh "$outdir"

BIN
screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB