MicroUI.cs/MicroUI.cs/Program.cs
2025-07-12 02:28:05 -04:00

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;
}
}