20 changed files with 1086 additions and 2 deletions
@ -0,0 +1,18 @@ |
|||
<Application |
|||
x:Class="Calc.App" |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:local="using:Calc" |
|||
xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Application.DataTemplates> |
|||
<local:ViewLocator /> |
|||
</Application.DataTemplates> |
|||
|
|||
<Application.Styles> |
|||
<themes:MaterialTheme |
|||
BaseTheme="Dark" |
|||
PrimaryColor="DeepPurple" |
|||
SecondaryColor="Lime" /> |
|||
<StyleInclude Source="avares://Material.Icons.Avalonia/App.xaml" /> |
|||
</Application.Styles> |
|||
</Application> |
|||
@ -0,0 +1,36 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls.ApplicationLifetimes; |
|||
using Avalonia.Markup.Xaml; |
|||
using Calc.ViewModels; |
|||
using Calc.Views; |
|||
|
|||
namespace Calc |
|||
{ |
|||
public partial class App : Application |
|||
{ |
|||
public override void Initialize() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
|
|||
public override void OnFrameworkInitializationCompleted() |
|||
{ |
|||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) |
|||
{ |
|||
desktop.MainWindow = new MainWindow |
|||
{ |
|||
DataContext = new MainWindowViewModel() |
|||
}; |
|||
} |
|||
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) |
|||
{ |
|||
singleView.MainView = new MainView |
|||
{ |
|||
DataContext = new MainWindowViewModel() |
|||
}; |
|||
} |
|||
|
|||
base.OnFrameworkInitializationCompleted(); |
|||
} |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 172 KiB |
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>net7.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<AvaloniaResource Include="Assets\**" /> |
|||
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" /> |
|||
<PackageReference Include="Material.Avalonia" Version="3.0.0-avalonia11-preview2" /> |
|||
<PackageReference Include="Material.Icons.Avalonia" Version="1.2.0" /> |
|||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" /> |
|||
<ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<Import Project="..\..\build\BuildTargets.targets" /> |
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
using System.IO; |
|||
|
|||
namespace Calc.Models; |
|||
|
|||
public class Calculation |
|||
{ |
|||
private readonly double _firstValue; |
|||
private readonly double _secondValue; |
|||
private readonly Operator? _operator; |
|||
|
|||
public Calculation(double firstValue, double secondValue, Operator? @operator) |
|||
{ |
|||
_firstValue = firstValue; |
|||
_secondValue = secondValue; |
|||
_operator = @operator; |
|||
} |
|||
|
|||
public double Calculate() |
|||
{ |
|||
return _operator switch |
|||
{ |
|||
Operator.Add => _firstValue + _secondValue, |
|||
Operator.Subtract => _firstValue - _secondValue, |
|||
Operator.Multiply => _firstValue * _secondValue, |
|||
Operator.Divide => _firstValue / _secondValue, |
|||
_ => throw new InvalidDataException("Operator not allowed") |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
|
|||
namespace Calc.Models; |
|||
|
|||
public static class Calculator |
|||
{ |
|||
public static string Calculate(string str) |
|||
{ |
|||
var calculus = new MyStringBuilder(str); |
|||
|
|||
var numberOfOpeningParentheses = calculus.Count('('); |
|||
var numberOfClosingParentheses = calculus.Count(')'); |
|||
|
|||
if (numberOfOpeningParentheses != numberOfClosingParentheses) |
|||
return "Waiting until all parentheses are closed"; |
|||
|
|||
CalculateParentheses(ref calculus); |
|||
CalculateNonParentheses(ref calculus); |
|||
|
|||
return calculus.ToString(); |
|||
} |
|||
|
|||
private static Operator? CharToOperator(char? character) |
|||
{ |
|||
return character switch |
|||
{ |
|||
OperatorChar.Add => Operator.Add, |
|||
OperatorChar.Subtract => Operator.Subtract, |
|||
OperatorChar.Multiply => Operator.Multiply, |
|||
OperatorChar.Divide => Operator.Divide, |
|||
_ => null |
|||
}; |
|||
} |
|||
|
|||
private static void CalculateNonParentheses(ref MyStringBuilder calculus) |
|||
{ |
|||
int indexOfOperator; |
|||
|
|||
// Search calculations with precedence first. When there isn't more, continue with the others
|
|||
while ((indexOfOperator = calculus.IndexOfAny(OperatorChar.PrecedentOperators, 1)) > 0 || |
|||
(indexOfOperator = calculus.IndexOfAny(OperatorChar.NonPrecedentOperators, 1)) > 0) |
|||
{ |
|||
// ==== Find the first operand ==== //
|
|||
var indexOfPreviousOperator = SetIndexOfPreviousOperator(calculus, indexOfOperator); |
|||
var stringOfFirstValue = calculus[(indexOfPreviousOperator + 1)..indexOfOperator]; |
|||
var startIndexOfCalculation = indexOfPreviousOperator + 1; |
|||
|
|||
// First value could be just the sign -, e.g. in --3
|
|||
if (stringOfFirstValue.Length == 1 && |
|||
OperatorChar.IsAnOperator(stringOfFirstValue[0])) |
|||
{ |
|||
stringOfFirstValue = "0"; |
|||
indexOfOperator--; // This way operator would be - and secondValue -3
|
|||
} |
|||
|
|||
// ==== Find the second operand ==== //
|
|||
// startIndex = indexOfOperator + 2 avoids to detect sign of second value as operator
|
|||
var indexOfNextOperator = calculus.IndexOfAny(OperatorChar.Operators, indexOfOperator + 2); |
|||
|
|||
if (indexOfNextOperator == -1) // Last calculation
|
|||
indexOfNextOperator = calculus.Length; |
|||
|
|||
var stringOfSecondValue = calculus[(indexOfOperator + 1)..indexOfNextOperator]; |
|||
var nextIndexAfterCalculation = indexOfNextOperator; |
|||
|
|||
// ==== Construct the calculation ==== //
|
|||
var firstValue = Convert.ToDouble(stringOfFirstValue); |
|||
var @operator = CharToOperator(calculus[indexOfOperator]); |
|||
var secondValue = Convert.ToDouble(stringOfSecondValue); |
|||
|
|||
var calculation = new Calculation(firstValue, secondValue, @operator); |
|||
|
|||
// Replace calculation with its result
|
|||
calculus.Replace(startIndexOfCalculation, nextIndexAfterCalculation, |
|||
Convert.ToString(calculation.Calculate(), CultureInfo.CurrentCulture)); |
|||
} |
|||
} |
|||
|
|||
private static void CalculateParentheses(ref MyStringBuilder calculus) |
|||
{ |
|||
int indexOfOpeningParenthesis; |
|||
while ((indexOfOpeningParenthesis = calculus.LastIndexOf('(')) != -1) |
|||
{ |
|||
var indexOfClosingParenthesis = calculus.IndexOf(')', indexOfOpeningParenthesis); |
|||
// Replace parentheses with its result
|
|||
calculus.Replace(indexOfOpeningParenthesis, indexOfClosingParenthesis + 1, |
|||
Calculate(calculus[(indexOfOpeningParenthesis + 1)..indexOfClosingParenthesis])); |
|||
} |
|||
} |
|||
|
|||
private static int SetIndexOfPreviousOperator(MyStringBuilder calculus, int indexOfOperator) |
|||
{ |
|||
var indexOfPreviousOperator = calculus.LastIndexOfAny(OperatorChar.Operators, indexOfOperator - 1); |
|||
|
|||
// First calculation. There could not be an operator at the beginning, it must be a sign
|
|||
if (indexOfPreviousOperator == 0) |
|||
{ |
|||
indexOfPreviousOperator = -1; |
|||
} |
|||
// If the first value is negative and not the first calculation, an operator must be just before the index
|
|||
// previously calculated as the indexOfPreviousOperator, e.g. in a+-b/c the - isn't previousOperator, it's +
|
|||
else if (indexOfPreviousOperator > 0 && |
|||
calculus[indexOfPreviousOperator].Equals(OperatorChar.Subtract) && // minus sign
|
|||
OperatorChar.IsAnOperator(calculus[indexOfPreviousOperator - 1])) // previous index contains an operator
|
|||
{ |
|||
indexOfPreviousOperator--; |
|||
} |
|||
|
|||
return indexOfPreviousOperator; |
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
using System; |
|||
using System.Text; |
|||
|
|||
namespace Calc.Models; |
|||
|
|||
public sealed class MyStringBuilder |
|||
{ |
|||
private readonly StringBuilder _stringBuilder = new(); |
|||
|
|||
public int Length => _stringBuilder.Length; |
|||
|
|||
public char this[int index] => _stringBuilder[index]; |
|||
|
|||
public char this[Index index] => _stringBuilder[index]; |
|||
|
|||
public string this[Range range] { |
|||
get |
|||
{ |
|||
var chain = new MyStringBuilder(); |
|||
|
|||
for (var i = range.Start.Value; i < range.End.Value; i++) |
|||
{ |
|||
chain.Append(this[i]); |
|||
} |
|||
|
|||
return chain.ToString(); |
|||
} |
|||
} |
|||
|
|||
public MyStringBuilder(){} |
|||
|
|||
public MyStringBuilder(string s) |
|||
{ |
|||
_stringBuilder.Append(s); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return _stringBuilder.ToString(); |
|||
} |
|||
|
|||
public MyStringBuilder Append<T>(T stuff) |
|||
{ |
|||
_stringBuilder.Append(stuff); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public MyStringBuilder Clear() |
|||
{ |
|||
_stringBuilder.Clear(); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public int Count(char character) |
|||
{ |
|||
char[] chars = { character }; |
|||
return Count(chars); |
|||
} |
|||
|
|||
public int Count(char[] chars) |
|||
{ |
|||
var count = 0; |
|||
var i = 0; |
|||
|
|||
while (i < Length) |
|||
{ |
|||
foreach (var character in chars) |
|||
{ |
|||
if (this[i].Equals(character)) |
|||
{ |
|||
count++; |
|||
break; |
|||
} |
|||
} |
|||
i++; |
|||
} |
|||
|
|||
return count; |
|||
} |
|||
|
|||
public int IndexOf(char character, int startIndex = 0) |
|||
{ |
|||
char[] chars = { character }; |
|||
return IndexOfAny(chars, startIndex); |
|||
} |
|||
|
|||
public int IndexOfAny(char[] chars, int startIndex = 0) |
|||
{ |
|||
var i = startIndex; |
|||
|
|||
while (i >= 0 && i < Length) |
|||
{ |
|||
foreach (var character in chars) |
|||
{ |
|||
if (this[i].Equals(character)) |
|||
return i; |
|||
} |
|||
i++; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
public MyStringBuilder Insert<T>(int index, T stuff) |
|||
{ |
|||
_stringBuilder.Insert(index, stuff); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public int LastIndexOf(char character) |
|||
{ |
|||
return LastIndexOf(character, Length - 1); |
|||
} |
|||
|
|||
public int LastIndexOf(char character, int startIndex) |
|||
{ |
|||
char[] chars = { character }; |
|||
return LastIndexOfAny(chars, startIndex); |
|||
} |
|||
|
|||
public int LastIndexOfAny(char[] chars) |
|||
{ |
|||
return LastIndexOfAny(chars, Length - 1); |
|||
} |
|||
|
|||
public int LastIndexOfAny(char[] chars, int startIndex) |
|||
{ |
|||
var i = startIndex; |
|||
while (i >= 0 && i < Length) |
|||
{ |
|||
foreach (var character in chars) |
|||
{ |
|||
if (this[i].Equals(character)) |
|||
return i; |
|||
} |
|||
i--; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
public MyStringBuilder Remove(int index, int length = 1) |
|||
{ |
|||
_stringBuilder.Remove(index, length); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public MyStringBuilder Replace<T>(int startIndex, int endIndex, T replacement) |
|||
{ |
|||
Remove(startIndex, endIndex - startIndex) |
|||
.Insert(startIndex, replacement); |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Calc.Models; |
|||
|
|||
public enum Operator |
|||
{ |
|||
Add, |
|||
Subtract, |
|||
Multiply, |
|||
Divide |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Linq; |
|||
|
|||
namespace Calc.Models; |
|||
|
|||
public static class OperatorChar |
|||
{ |
|||
public const char Add = '+'; |
|||
public const char Subtract = '-'; |
|||
public const char Multiply = '*'; |
|||
public const char Divide = '/'; |
|||
|
|||
public static readonly char[] Operators = { Add, Subtract, Multiply, Divide }; |
|||
public static readonly char[] PrecedentOperators = { Multiply, Divide }; |
|||
public static readonly char[] NonPrecedentOperators = { Add, Subtract }; |
|||
|
|||
public static bool IsAnOperator(char character) |
|||
{ |
|||
return Operators.Contains(character); |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Templates; |
|||
using Calc.ViewModels; |
|||
|
|||
namespace Calc |
|||
{ |
|||
public class ViewLocator : IDataTemplate |
|||
{ |
|||
public IControl Build(object data) |
|||
{ |
|||
var name = data.GetType().FullName!.Replace("ViewModel", "View"); |
|||
var type = Type.GetType(name); |
|||
|
|||
if (type != null) |
|||
{ |
|||
return (Control)Activator.CreateInstance(type)!; |
|||
} |
|||
|
|||
return new TextBlock { Text = "Not Found: " + name }; |
|||
} |
|||
|
|||
public bool Match(object data) |
|||
{ |
|||
return data is ViewModelBase; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Reactive; |
|||
using Calc.Models; |
|||
using ReactiveUI; |
|||
|
|||
namespace Calc.ViewModels |
|||
{ |
|||
public class MainWindowViewModel : ViewModelBase |
|||
{ |
|||
private string _shownString = string.Empty; |
|||
private string _shownResult = string.Empty; |
|||
private int _numberOfOpeningParentheses; |
|||
private int _numberOfClosingParentheses; |
|||
|
|||
// Commands
|
|||
public ReactiveCommand<Unit, Unit> AddDecimalSeparatorCommand { get; } |
|||
public ReactiveCommand<int, Unit> AddNumberCommand { get; } |
|||
public ReactiveCommand<Operator, Unit> AddOperatorCommand { get; } |
|||
public ReactiveCommand<Unit, Unit> AddParenthesisCommand { get; } |
|||
public ReactiveCommand<Unit, Unit> AlternateNegativePositiveCommand { get; } |
|||
public ReactiveCommand<Unit, Unit> ClearScreenCommand { get; } |
|||
public ReactiveCommand<Unit, Unit> DeleteLastCommand { get; } |
|||
public ReactiveCommand<Unit, Unit> PickResultCommand { get; } |
|||
|
|||
public MainWindowViewModel() |
|||
{ |
|||
AddDecimalSeparatorCommand = ReactiveCommand.Create(AddDecimalSeparator); |
|||
AddNumberCommand = ReactiveCommand.Create<int>(AddNumber); |
|||
AddOperatorCommand = ReactiveCommand.Create<Operator>(AddOperator); |
|||
AddParenthesisCommand = ReactiveCommand.Create(AddParenthesis); |
|||
AlternateNegativePositiveCommand = ReactiveCommand.Create(AlternateNegativePositive); |
|||
ClearScreenCommand = ReactiveCommand.Create(ClearScreen); |
|||
DeleteLastCommand = ReactiveCommand.Create(DeleteLast); |
|||
PickResultCommand = ReactiveCommand.Create(PickResult); |
|||
} |
|||
|
|||
public string ShownString |
|||
{ |
|||
get => _shownString; |
|||
set => this.RaiseAndSetIfChanged(ref _shownString, value); |
|||
} |
|||
|
|||
public string ShownResult |
|||
{ |
|||
get => _shownResult; |
|||
set => this.RaiseAndSetIfChanged(ref _shownResult, value); |
|||
} |
|||
|
|||
private void AddDecimalSeparator() |
|||
{ |
|||
if (CanDecimalSeparatorBePlaced()) |
|||
ShownString += CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; |
|||
} |
|||
|
|||
private void AddNumber(int value) |
|||
{ |
|||
ShownString += value; |
|||
Calculate(ShownString); |
|||
} |
|||
|
|||
private void AddOperator(Operator @operator) |
|||
{ |
|||
if (ShownString[^1].Equals('(')) |
|||
return; |
|||
|
|||
if (IsLastInputAnOperator()) |
|||
ShownString = ShownString[..^1]; |
|||
|
|||
ShownString += @operator switch |
|||
{ |
|||
Operator.Add => OperatorChar.Add, |
|||
Operator.Subtract => OperatorChar.Subtract, |
|||
Operator.Multiply => OperatorChar.Multiply, |
|||
Operator.Divide => OperatorChar.Divide, |
|||
_ => throw new InvalidDataException("Operator not allowed") |
|||
}; |
|||
} |
|||
|
|||
private void AddParenthesis() |
|||
{ |
|||
if (ShownString.Length == 0 || IsLastInputAnOperator() || ShownString[^1].Equals('(')) |
|||
{ |
|||
ShownString += "("; |
|||
_numberOfOpeningParentheses++; |
|||
} |
|||
else if (_numberOfClosingParentheses < _numberOfOpeningParentheses) |
|||
{ |
|||
ShownString += ")"; |
|||
_numberOfClosingParentheses++; |
|||
Calculate(ShownString); |
|||
} |
|||
} |
|||
|
|||
private void AlternateNegativePositive() |
|||
{ |
|||
var indexWhereSetOrUnsetSign = SetIndexWhereToSetOrUnsetSign(); |
|||
|
|||
if (ShownString.Length == 0 || ShownString[^1].Equals('(')) |
|||
ShownString += OperatorChar.Subtract; |
|||
else |
|||
{ |
|||
switch (ShownString[indexWhereSetOrUnsetSign]) |
|||
{ |
|||
case OperatorChar.Subtract: |
|||
if (indexWhereSetOrUnsetSign == 0 || |
|||
ShownString[indexWhereSetOrUnsetSign - 1].Equals('(') || |
|||
OperatorChar.IsAnOperator(ShownString[indexWhereSetOrUnsetSign - 1])) |
|||
|
|||
ShownString = ShownString.Remove(indexWhereSetOrUnsetSign, 1); |
|||
else |
|||
// Add -
|
|||
ShownString = ShownString[..indexWhereSetOrUnsetSign] + |
|||
OperatorChar.Subtract + |
|||
ShownString[indexWhereSetOrUnsetSign..]; |
|||
break; |
|||
default: |
|||
// Add -
|
|||
ShownString = ShownString[..indexWhereSetOrUnsetSign] + |
|||
OperatorChar.Subtract + |
|||
ShownString[indexWhereSetOrUnsetSign..]; |
|||
break; |
|||
} |
|||
|
|||
Calculate(ShownString); |
|||
} |
|||
} |
|||
|
|||
private void Calculate(string calc) |
|||
{ |
|||
ShownResult = Calculator.Calculate(calc); |
|||
} |
|||
|
|||
private bool CanDecimalSeparatorBePlaced() |
|||
{ |
|||
var indexLastDecimalSeparator = ShownString.LastIndexOf( |
|||
CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, |
|||
StringComparison.Ordinal); |
|||
var indexLastOperator = ShownString.LastIndexOfAny(OperatorChar.Operators); |
|||
|
|||
if (indexLastDecimalSeparator == -1 && indexLastOperator == -1) |
|||
return true; |
|||
|
|||
return indexLastOperator > indexLastDecimalSeparator; |
|||
} |
|||
|
|||
private void ClearScreen() |
|||
{ |
|||
ShownString = string.Empty; |
|||
ShownResult = string.Empty; |
|||
_numberOfOpeningParentheses = 0; |
|||
_numberOfClosingParentheses = 0; |
|||
} |
|||
|
|||
private void DeleteLast() |
|||
{ |
|||
if (ShownString.Length == 1) |
|||
{ |
|||
ClearScreen(); |
|||
return; |
|||
} |
|||
|
|||
// Update number of parentheses
|
|||
switch (ShownString[^1]) |
|||
{ |
|||
case '(': |
|||
_numberOfOpeningParentheses--; |
|||
break; |
|||
case ')': |
|||
_numberOfClosingParentheses--; |
|||
break; |
|||
} |
|||
|
|||
ShownString = ShownString[..^1]; |
|||
Calculate(IsLastInputAnOperator() ? ShownString[..^1] : ShownString); |
|||
} |
|||
|
|||
private bool IsLastInputAnOperator() |
|||
{ |
|||
return OperatorChar.IsAnOperator(ShownString[^1]); |
|||
} |
|||
|
|||
private static int MaxOf(int number1, int number2) |
|||
{ |
|||
return number1 > number2 ? number1 : number2; |
|||
} |
|||
|
|||
private void PickResult() |
|||
{ |
|||
ShownString = ShownResult; |
|||
ShownResult = string.Empty; |
|||
} |
|||
|
|||
private int SetIndexWhereToSetOrUnsetSign() |
|||
{ |
|||
char[] nonSubstractOperators = { OperatorChar.Add, OperatorChar.Multiply, OperatorChar.Divide }; |
|||
|
|||
var indexAfterLastNonSubstractOperator = ShownString.LastIndexOfAny(nonSubstractOperators) + 1; |
|||
var indexOfLastSubstractOperator = ShownString.LastIndexOf(OperatorChar.Subtract); |
|||
var indexAfterLastParenthesis = ShownString.LastIndexOf('(') + 1; |
|||
var indexLastOperator = MaxOf(indexAfterLastNonSubstractOperator, indexOfLastSubstractOperator); |
|||
|
|||
return MaxOf(indexAfterLastParenthesis, indexLastOperator); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using ReactiveUI; |
|||
|
|||
namespace Calc.ViewModels |
|||
{ |
|||
public class ViewModelBase : ReactiveObject |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,377 @@ |
|||
<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:material="using:Material.Icons.Avalonia" |
|||
xmlns:system="clr-namespace:System;assembly=System.Runtime" |
|||
xmlns:viewModels="clr-namespace:Calc.ViewModels" |
|||
MaxWidth="800" MaxHeight="500" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" |
|||
xmlns:m="using:Calc.Models" |
|||
mc:Ignorable="d" |
|||
x:Class="Calc.Views.MainView" |
|||
x:DataType="viewModels:MainWindowViewModel" |
|||
x:CompileBindings="False"> |
|||
<UserControl.Styles> |
|||
<Style Selector="Button"> |
|||
<Setter Property="Margin" Value="5" /> |
|||
</Style> |
|||
<Style Selector="Button TextBlock"> |
|||
<Setter Property="FontSize" Value="20" /> |
|||
</Style> |
|||
<Style Selector="TextBlock.screen"> |
|||
<Setter Property="Background" Value="#2f2a2c" /> |
|||
<Setter Property="Margin" Value="5" /> |
|||
<Setter Property="FontSize" Value="30" /> |
|||
<Setter Property="MinHeight" Value="35" /> |
|||
<Setter Property="TextAlignment" Value="Right" /> |
|||
<Setter Property="Foreground" Value="#ddffffff" /> |
|||
</Style> |
|||
</UserControl.Styles> |
|||
<UserControl.KeyBindings> |
|||
<!-- 0 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D0"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>0</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad0"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>0</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 1 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D1"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>1</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad1"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>1</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 2 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D2"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>2</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad2"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>2</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 3 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D3"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>3</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad3"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>3</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 4 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D4"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>4</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad4"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>4</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 5 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D5"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>5</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad5"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>5</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 6 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D6"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>6</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad6"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>6</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 7 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D7"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>7</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad7"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>7</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 8 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D8"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>8</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad8"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>8</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- 9 --> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="D9"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>9</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddNumber}" Gesture="NumPad9"> |
|||
<KeyBinding.CommandParameter> |
|||
<system:Int32>9</system:Int32> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- + --> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="Add"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Add</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="OemPlus"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Add</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- - --> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="Subtract"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Subtract</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="OemMinus"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Subtract</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- . --> |
|||
<KeyBinding Command="{Binding AddDecimalSeparatorCommand}" Gesture="OemPeriod" /> |
|||
<!-- +- --> |
|||
<KeyBinding Command="{Binding AlternateNegativePositiveCommand}" Gesture="Alt+Subtract" /> |
|||
<KeyBinding Command="{Binding AlternateNegativePositiveCommand}" Gesture="Alt+OemMinus" /> |
|||
<!-- Backspace --> |
|||
<KeyBinding Command="{Binding DeleteLastCommand}" Gesture="Back" /> |
|||
<!-- Delete --> |
|||
<KeyBinding Command="{Binding ClearScreenCommand}" Gesture="Escape" /> |
|||
<KeyBinding Command="{Binding ClearScreenCommand}" Gesture="Delete" /> |
|||
<!-- * --> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="Multiply"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Multiply</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- / --> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="Divide"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Divide</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<KeyBinding Command="{Binding AddOperatorCommand}" Gesture="Oem2"> |
|||
<KeyBinding.CommandParameter> |
|||
<m:Operator>Divide</m:Operator> |
|||
</KeyBinding.CommandParameter> |
|||
</KeyBinding> |
|||
<!-- () --> |
|||
<KeyBinding Command="{Binding AddParenthesisCommand}" Gesture="OemOpenBrackets" /> |
|||
<KeyBinding Command="{Binding AddParenthesisCommand}" Gesture="OemCloseBrackets" /> |
|||
<!-- = --> |
|||
<KeyBinding Command="{Binding PickResultCommand}" Gesture="Enter" /> |
|||
</UserControl.KeyBindings> |
|||
<Grid Margin="5" RowDefinitions="Auto, Auto, *"> |
|||
<!-- Screens --> |
|||
<TextBlock |
|||
Classes="screen" |
|||
Grid.Row="0" |
|||
Text="{Binding ShownString}" /> |
|||
<TextBlock |
|||
Classes="screen" |
|||
Grid.Row="1" |
|||
Text="{Binding ShownResult}" /> |
|||
<!-- Keys --> |
|||
<Grid |
|||
ColumnDefinitions="*,*,*,*,*" |
|||
Grid.Row="2" |
|||
RowDefinitions="*,*,*,*"> |
|||
<!-- Numeric keys --> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="0" |
|||
Grid.Row="3"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>0</system:Int32> |
|||
</Button.CommandParameter> |
|||
0 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="0" |
|||
Grid.Row="2"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>1</system:Int32> |
|||
</Button.CommandParameter> |
|||
1 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="1" |
|||
Grid.Row="2"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>2</system:Int32> |
|||
</Button.CommandParameter> |
|||
2 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="2" |
|||
Grid.Row="2"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>3</system:Int32> |
|||
</Button.CommandParameter> |
|||
3 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="0" |
|||
Grid.Row="1"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>4</system:Int32> |
|||
</Button.CommandParameter> |
|||
4 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="1" |
|||
Grid.Row="1"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>5</system:Int32> |
|||
</Button.CommandParameter> |
|||
5 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="2" |
|||
Grid.Row="1"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>6</system:Int32> |
|||
</Button.CommandParameter> |
|||
6 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="0" |
|||
Grid.Row="0"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>7</system:Int32> |
|||
</Button.CommandParameter> |
|||
7 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="1" |
|||
Grid.Row="0"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>8</system:Int32> |
|||
</Button.CommandParameter> |
|||
8 |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddNumber}" |
|||
Grid.Column="2" |
|||
Grid.Row="0"> |
|||
<Button.CommandParameter> |
|||
<system:Int32>9</system:Int32> |
|||
</Button.CommandParameter> |
|||
9 |
|||
</Button> |
|||
<!-- Non-numeric keys --> |
|||
<Button |
|||
Command="{Binding AddDecimalSeparator}" |
|||
Grid.Column="1" |
|||
Grid.Row="3"> |
|||
. |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AlternateNegativePositive}" |
|||
Grid.Column="2" |
|||
Grid.Row="3"> |
|||
+/- |
|||
</Button> |
|||
<Button |
|||
Command="{Binding DeleteLast}" |
|||
Grid.Column="3" |
|||
Grid.Row="0"> |
|||
<material:MaterialIcon Kind="Backspace" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding ClearScreen}" |
|||
Grid.Column="4" |
|||
Grid.Row="0"> |
|||
<material:MaterialIcon Kind="DeleteForever" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddOperator}" |
|||
Grid.Column="3" |
|||
Grid.Row="1"> |
|||
<Button.CommandParameter> |
|||
<m:Operator>Multiply</m:Operator> |
|||
</Button.CommandParameter> |
|||
<material:MaterialIcon Kind="Multiply" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddOperator}" |
|||
Grid.Column="4" |
|||
Grid.Row="1"> |
|||
<Button.CommandParameter> |
|||
<m:Operator>Divide</m:Operator> |
|||
</Button.CommandParameter> |
|||
<material:MaterialIcon Kind="Division" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddOperator}" |
|||
Grid.Column="3" |
|||
Grid.Row="2"> |
|||
<Button.CommandParameter> |
|||
<m:Operator>Add</m:Operator> |
|||
</Button.CommandParameter> |
|||
<material:MaterialIcon Kind="Add" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddOperator}" |
|||
Grid.Column="4" |
|||
Grid.Row="2"> |
|||
<Button.CommandParameter> |
|||
<m:Operator>Subtract</m:Operator> |
|||
</Button.CommandParameter> |
|||
<material:MaterialIcon Kind="Minus" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding AddParenthesis}" |
|||
Grid.Column="3" |
|||
Grid.Row="3"> |
|||
<material:MaterialIcon Kind="CodeParentheses" /> |
|||
</Button> |
|||
<Button |
|||
Command="{Binding PickResult}" |
|||
Grid.Column="4" |
|||
Grid.Row="3"> |
|||
<material:MaterialIcon Kind="Equal" /> |
|||
</Button> |
|||
</Grid> |
|||
</Grid> |
|||
</UserControl> |
|||
@ -0,0 +1,25 @@ |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace Calc.Views; |
|||
|
|||
public partial class MainView : UserControl |
|||
{ |
|||
public MainView() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
protected override void OnLoaded() |
|||
{ |
|||
base.OnLoaded(); |
|||
|
|||
Focus(); |
|||
} |
|||
|
|||
private void InitializeComponent() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
<Window |
|||
Height="500" |
|||
Icon="/Assets/avalonia-logo.ico" |
|||
Title="Calc" |
|||
Width="600" |
|||
d:DesignHeight="450" |
|||
d:DesignWidth="600" |
|||
mc:Ignorable="d" |
|||
x:Class="Calc.Views.MainWindow" |
|||
xmlns="https://github.com/avaloniaui" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:views="clr-namespace:Calc.Views" Focusable="False"> |
|||
<views:MainView Focusable="True" /> |
|||
</Window> |
|||
@ -0,0 +1,12 @@ |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Calc.Views |
|||
{ |
|||
public partial class MainWindow : Window |
|||
{ |
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue