354 lines
12 KiB
C#
354 lines
12 KiB
C#
using Raylib_cs;
|
|
using MicroUI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace MicroUI;
|
|
|
|
class Program
|
|
{
|
|
private static string display = "0";
|
|
private static string expression = "";
|
|
private static string lastOperation = "";
|
|
private static double lastResult = 0;
|
|
private static bool evaluated = false;
|
|
private static string currentInput = "";
|
|
|
|
private static readonly string[,] keys =
|
|
{
|
|
{ "7", "8", "9", "/" },
|
|
{ "4", "5", "6", "*" },
|
|
{ "1", "2", "3", "-" },
|
|
{ "0", "(", ")", "+" }
|
|
};
|
|
|
|
[STAThread]
|
|
public static void Main()
|
|
{
|
|
const int winW = 300;
|
|
const int winH = 450;
|
|
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
|
|
Raylib.InitWindow(800, 600, "Calculator");
|
|
var ctx = new MuContext();
|
|
ctx.TextWidth = (object font, string str, int len) => Raylib.MeasureText(str, 20);
|
|
ctx.TextHeight = (object font) => 24;
|
|
MicroUI.Init(ctx);
|
|
|
|
while (!Raylib.WindowShouldClose())
|
|
{
|
|
var mousePos = Raylib.GetMousePosition();
|
|
MuInput.MouseMove(ctx, (int)mousePos.X, (int)mousePos.Y);
|
|
if (Raylib.IsMouseButtonPressed(Raylib_cs.MouseButton.Left))
|
|
MuInput.MouseDown(ctx, (int)mousePos.X, (int)mousePos.Y, (int)MouseButton.Left);
|
|
if (Raylib.IsMouseButtonReleased(Raylib_cs.MouseButton.Left))
|
|
MuInput.MouseUp(ctx, (int)mousePos.X, (int)mousePos.Y, (int)MouseButton.Left);
|
|
|
|
Raylib.BeginDrawing();
|
|
Raylib.ClearBackground(Color.DarkGray);
|
|
|
|
MicroUI.Begin(ctx);
|
|
|
|
var winX = (Raylib.GetScreenWidth() - winW) / 2;
|
|
var winY = (Raylib.GetScreenHeight() - winH) / 2;
|
|
if (MuControl.BeginWindowEx(ctx, "Calculator", new MuRect(winX, winY, winW, winH), (int)Options.NoClose))
|
|
{
|
|
var container = MicroUI.GetCurrentContainer(ctx);
|
|
var availableWidth = container.Body.W - 32;
|
|
var buttonWidth = availableWidth / 4;
|
|
var buttonHeight = 48;
|
|
|
|
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { availableWidth }, 48);
|
|
string toShow = display == "Error" ? "Error" : (evaluated ? display : (expression.Length > 0 ? expression : "0"));
|
|
MuRect displayRect = MuLayoutUtil.LayoutNext(ctx);
|
|
MuControl.DrawControlText(ctx, toShow, displayRect, ColorType.Text, (int)Options.AlignRight);
|
|
|
|
for (int row = 0; row < 4; row++)
|
|
{
|
|
MuLayoutUtil.LayoutRow(ctx, 4, new int[] { buttonWidth, buttonWidth, buttonWidth, buttonWidth }, buttonHeight);
|
|
for (int col = 0; col < 4; col++)
|
|
{
|
|
string key = keys[row, col];
|
|
if (MuControl.ButtonEx(ctx, key, 0, (int)Options.AlignCenter).Flags.HasFlag(ResultFlags.Submit))
|
|
HandleKey(key);
|
|
}
|
|
}
|
|
|
|
MuLayoutUtil.LayoutRow(ctx, 4, new int[] { buttonWidth, buttonWidth, buttonWidth, buttonWidth }, buttonHeight);
|
|
if (MuControl.ButtonEx(ctx, "C", 0, (int)Options.AlignCenter).Flags.HasFlag(ResultFlags.Submit)) HandleKey("C");
|
|
if (MuControl.ButtonEx(ctx, ".", 0, (int)Options.AlignCenter).Flags.HasFlag(ResultFlags.Submit)) HandleKey(".");
|
|
if (MuControl.ButtonEx(ctx, "<-", 0, (int)Options.AlignCenter).Flags.HasFlag(ResultFlags.Submit)) HandleKey("<-");
|
|
if (MuControl.ButtonEx(ctx, "=", 0, (int)Options.AlignCenter).Flags.HasFlag(ResultFlags.Submit)) HandleKey("=");
|
|
|
|
MuControl.EndWindow(ctx);
|
|
}
|
|
|
|
MicroUI.End(ctx);
|
|
|
|
int cmdIndex = 0;
|
|
MuCommand? cmd = null;
|
|
while ((cmd = MuCommandList.NextCommand(ctx, ref cmdIndex)) != null)
|
|
{
|
|
switch (cmd)
|
|
{
|
|
case MuRectCommand rectCmd:
|
|
Raylib.DrawRectangle(rectCmd.Rect.X, rectCmd.Rect.Y, rectCmd.Rect.W, rectCmd.Rect.H, new Color(rectCmd.Color.R, rectCmd.Color.G, rectCmd.Color.B, rectCmd.Color.A));
|
|
break;
|
|
case MuClipCommand clipCmd:
|
|
Raylib.BeginScissorMode(clipCmd.Rect.X, clipCmd.Rect.Y, clipCmd.Rect.W, clipCmd.Rect.H);
|
|
break;
|
|
case MuTextCommand textCmd:
|
|
Raylib.DrawText(textCmd.Text, textCmd.Position.X, textCmd.Position.Y, 20, new Color(textCmd.Color.R, textCmd.Color.G, textCmd.Color.B, textCmd.Color.A));
|
|
break;
|
|
}
|
|
}
|
|
|
|
Raylib.EndDrawing();
|
|
}
|
|
Raylib.CloseWindow();
|
|
}
|
|
|
|
private static void HandleKey(string key)
|
|
{
|
|
if (key == "<-")
|
|
{
|
|
if (evaluated)
|
|
{
|
|
display = "0";
|
|
currentInput = "";
|
|
expression = "";
|
|
evaluated = false;
|
|
}
|
|
else if (expression.Length > 0)
|
|
{
|
|
if (expression.EndsWith(" "))
|
|
{
|
|
expression = expression[..^3];
|
|
}
|
|
else
|
|
{
|
|
expression = expression[..^1];
|
|
}
|
|
if (currentInput.Length > 0)
|
|
currentInput = currentInput[..^1];
|
|
if (expression.Length == 0)
|
|
display = "0";
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (char.IsDigit(key, 0) || key == ".")
|
|
{
|
|
if (evaluated)
|
|
{
|
|
expression = "";
|
|
evaluated = false;
|
|
}
|
|
if (key == "." && currentInput.Contains(".")) return;
|
|
currentInput += key;
|
|
expression += key;
|
|
return;
|
|
}
|
|
|
|
if (key == "C")
|
|
{
|
|
display = "0";
|
|
currentInput = "";
|
|
expression = "";
|
|
evaluated = false;
|
|
return;
|
|
}
|
|
|
|
if (key == "=")
|
|
{
|
|
if (expression.Length > 0 && !IsOperator(expression[^1]) && expression[^1] != '(' && !evaluated)
|
|
{
|
|
try
|
|
{
|
|
var result = EvaluateExpression(expression);
|
|
display = FormatResult(result);
|
|
lastResult = result;
|
|
lastOperation = expression;
|
|
currentInput = "";
|
|
expression = "";
|
|
evaluated = true;
|
|
}
|
|
catch
|
|
{
|
|
display = "Error";
|
|
}
|
|
}
|
|
else if (evaluated && lastOperation.Length > 0)
|
|
{
|
|
try
|
|
{
|
|
var tokens = Tokenize(lastOperation);
|
|
if (tokens.Count == 3 && tokens[1] is "+" or "-" or "*" or "/")
|
|
{
|
|
var op = tokens[1];
|
|
var right = tokens[2];
|
|
var newExpr = $"{lastResult} {op} {right}";
|
|
var result = EvaluateExpression(newExpr);
|
|
display = FormatResult(result);
|
|
lastResult = result;
|
|
expression = newExpr;
|
|
}
|
|
else
|
|
{
|
|
string op = "";
|
|
string right = "";
|
|
for (int i = tokens.Count - 1; i >= 0; i--)
|
|
{
|
|
if (tokens[i] is "+" or "-" or "*" or "/")
|
|
{
|
|
op = tokens[i];
|
|
right = lastResult.ToString();
|
|
break;
|
|
}
|
|
}
|
|
if (!string.IsNullOrEmpty(op))
|
|
{
|
|
var newExpr = $"{lastResult} {op} {right}";
|
|
var result = EvaluateExpression(newExpr);
|
|
display = FormatResult(result);
|
|
lastResult = result;
|
|
expression = newExpr;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
display = "Error";
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (key is "(" or ")")
|
|
{
|
|
if (evaluated)
|
|
{
|
|
expression = "";
|
|
evaluated = false;
|
|
}
|
|
expression += key;
|
|
currentInput = "";
|
|
return;
|
|
}
|
|
|
|
if (expression.Length == 0 && key == "-")
|
|
{
|
|
currentInput = "-";
|
|
expression = "-";
|
|
return;
|
|
}
|
|
if (expression.Length > 0 && !IsOperator(expression[^1]) && expression[^1] != '(')
|
|
{
|
|
expression += " " + key + " ";
|
|
currentInput = "";
|
|
evaluated = false;
|
|
}
|
|
}
|
|
|
|
private static bool IsOperator(char c) => c is '+' or '-' or '*' or '/';
|
|
|
|
private static double EvaluateExpression(string expr)
|
|
{
|
|
var tokens = Tokenize(expr);
|
|
int idx = 0;
|
|
var result = ParseExpression(tokens, ref idx);
|
|
if (idx < tokens.Count)
|
|
throw new Exception("Unexpected token: " + tokens[idx]);
|
|
return result;
|
|
}
|
|
|
|
private static List<string> Tokenize(string expr)
|
|
{
|
|
var tokens = new List<string>();
|
|
var current = "";
|
|
foreach (var c in expr)
|
|
{
|
|
if (char.IsDigit(c) || c == '.')
|
|
current += c;
|
|
else if (c is '+' or '-' or '*' or '/' or '(' or ')')
|
|
{
|
|
if (current.Length > 0)
|
|
{
|
|
tokens.Add(current);
|
|
current = "";
|
|
}
|
|
tokens.Add(c.ToString());
|
|
}
|
|
else if (c == ' ')
|
|
{
|
|
if (current.Length > 0)
|
|
{
|
|
tokens.Add(current);
|
|
current = "";
|
|
}
|
|
}
|
|
}
|
|
if (current.Length > 0)
|
|
tokens.Add(current);
|
|
return tokens;
|
|
}
|
|
|
|
private static double ParseExpression(List<string> tokens, ref int idx)
|
|
{
|
|
var left = ParseTerm(tokens, ref idx);
|
|
while (idx < tokens.Count && (tokens[idx] == "+" || tokens[idx] == "-"))
|
|
{
|
|
var op = tokens[idx++];
|
|
var right = ParseTerm(tokens, ref idx);
|
|
left = op == "+" ? left + right : left - right;
|
|
}
|
|
return left;
|
|
}
|
|
|
|
private static double ParseTerm(List<string> tokens, ref int idx)
|
|
{
|
|
var left = ParseFactor(tokens, ref idx);
|
|
while (idx < tokens.Count && (tokens[idx] == "*" || tokens[idx] == "/"))
|
|
{
|
|
var op = tokens[idx++];
|
|
var right = ParseFactor(tokens, ref idx);
|
|
if (op == "*") left *= right;
|
|
else if (op == "/" && right != 0) left /= right;
|
|
else throw new DivideByZeroException();
|
|
}
|
|
return left;
|
|
}
|
|
|
|
private static double ParseFactor(List<string> tokens, ref int idx)
|
|
{
|
|
if (idx >= tokens.Count)
|
|
throw new Exception("Unexpected end of expression");
|
|
var token = tokens[idx++];
|
|
if (token == "(")
|
|
{
|
|
var result = ParseExpression(tokens, ref idx);
|
|
if (idx >= tokens.Count || tokens[idx] != ")")
|
|
throw new Exception("Missing closing parenthesis");
|
|
idx++;
|
|
return result;
|
|
}
|
|
if (double.TryParse(token, out double number))
|
|
return number;
|
|
throw new Exception($"Invalid token: {token}");
|
|
}
|
|
|
|
private static string FormatResult(double value)
|
|
{
|
|
value = Math.Round(value, 10);
|
|
if (Math.Abs(value - Math.Round(value)) < 1e-10)
|
|
value = Math.Round(value);
|
|
var s = value.ToString("G15");
|
|
if (s.Contains("."))
|
|
{
|
|
s = s.TrimEnd('0');
|
|
if (s.EndsWith("."))
|
|
s = s.TrimEnd('.');
|
|
}
|
|
return s;
|
|
}
|
|
} |