Basic Calculator with expression parsing
This commit is contained in:
parent
cdc54751a3
commit
5de6c6a212
@ -10,15 +10,15 @@ namespace MicroUI
|
||||
{
|
||||
public const string Version = "1.0.0";
|
||||
public const int CommandListSize = 256 * 1024;
|
||||
public const int RootListSize = 32;
|
||||
public const int ContainerStackSize = 32;
|
||||
public const int ClipStackSize = 32;
|
||||
public const int IdStackSize = 32;
|
||||
public const int LayoutStackSize = 16;
|
||||
public const int ContainerPoolSize = 48;
|
||||
public const int TreeNodePoolSize = 48;
|
||||
public const int MaxWidths = 16;
|
||||
public const int MaxFormatLength = 127;
|
||||
public const int RootListSize = 256;
|
||||
public const int ContainerStackSize = 256;
|
||||
public const int ClipStackSize = 256;
|
||||
public const int IdStackSize = 256;
|
||||
public const int LayoutStackSize = 256;
|
||||
public const int ContainerPoolSize = 256;
|
||||
public const int TreeNodePoolSize = 256;
|
||||
public const int MaxWidths = 256;
|
||||
public const int MaxFormatLength = 256;
|
||||
}
|
||||
|
||||
public static class Format
|
||||
|
||||
@ -1,324 +1,347 @@
|
||||
using Raylib_cs;
|
||||
using MicroUI;
|
||||
using System.Numerics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MicroUI;
|
||||
|
||||
class Program
|
||||
{
|
||||
// Key state tracking for key up events
|
||||
private static HashSet<int> pressedKeys = new HashSet<int>();
|
||||
// Persistent textbox value
|
||||
private static string textboxValue = "test";
|
||||
// Second number value for testing multiple number controls
|
||||
private static float secondValue = 25.0f;
|
||||
// Track focus changes
|
||||
private static uint lastFocus = 0;
|
||||
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 is required if you deploy using NativeAOT on Windows - See https://github.com/raylib-cs/raylib-cs/issues/301
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
Raylib.InitWindow(800, 480, "Hello World");
|
||||
|
||||
// Create microui context
|
||||
var ctx = new MuContext();
|
||||
|
||||
// Set up required callbacks
|
||||
ctx.TextWidth = (object font, string str, int len) =>
|
||||
{
|
||||
// Use Raylib's MeasureText to get actual text width
|
||||
// If font is null, use default font, otherwise cast to Font
|
||||
Font textFont = font as Font? ?? Raylib.GetFontDefault();
|
||||
return Raylib.MeasureText(str, 10); // 16 is font size
|
||||
};
|
||||
|
||||
ctx.TextHeight = (object font) =>
|
||||
{
|
||||
// Use Raylib's font height measurement
|
||||
Font textFont = font as Font? ?? Raylib.GetFontDefault();
|
||||
return 10; // Return a fixed height for now, or use textFont.baseSize if available
|
||||
};
|
||||
// Initialize microui
|
||||
MicroUI.Init(ctx);
|
||||
|
||||
bool isChecked = false;
|
||||
float sliderValue = 50.0f; // Remove persistent slider value
|
||||
|
||||
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())
|
||||
{
|
||||
// Handle input
|
||||
var mousePos = Raylib.GetMousePosition();
|
||||
MuInput.MouseMove(ctx, (int)mousePos.X, (int)mousePos.Y);
|
||||
|
||||
// Track focus changes in main loop
|
||||
if (ctx.Focus != lastFocus)
|
||||
{
|
||||
lastFocus = ctx.Focus;
|
||||
}
|
||||
|
||||
if (Raylib.IsMouseButtonPressed(Raylib_cs.MouseButton.Left))
|
||||
{
|
||||
MuInput.MouseDown(ctx, (int)mousePos.X, (int)mousePos.Y, 1);
|
||||
}
|
||||
else if (Raylib.IsMouseButtonDown(Raylib_cs.MouseButton.Left))
|
||||
{
|
||||
// Keep MouseDown state active while button is held
|
||||
ctx.MouseDown = 1;
|
||||
}
|
||||
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, 1);
|
||||
}
|
||||
|
||||
// Handle keyboard input - only for modifier keys
|
||||
int key = Raylib.GetKeyPressed();
|
||||
if (key != 0)
|
||||
{
|
||||
// Only handle modifier keys here, not regular characters
|
||||
// Regular characters are handled in the text input section below
|
||||
if (key == (int)KeyboardKey.Enter || key == (int)KeyboardKey.Backspace)
|
||||
{
|
||||
// Convert to MicroUI key modifiers
|
||||
int muKey = 0;
|
||||
if (key == (int)KeyboardKey.Enter) muKey = (int)KeyModifiers.Return;
|
||||
if (key == (int)KeyboardKey.Backspace) muKey = (int)KeyModifiers.Backspace;
|
||||
|
||||
MuInput.KeyDown(ctx, muKey);
|
||||
pressedKeys.Add(muKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Shift key state (for Shift+Click functionality)
|
||||
if (Raylib.IsKeyDown(KeyboardKey.LeftShift) || Raylib.IsKeyDown(KeyboardKey.RightShift))
|
||||
{
|
||||
ctx.KeyDown |= (int)KeyModifiers.Shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.KeyDown &= ~(int)KeyModifiers.Shift;
|
||||
}
|
||||
|
||||
// Handle key up events for modifier keys
|
||||
var keysToRemove = new List<int>();
|
||||
foreach (int pressedKey in pressedKeys)
|
||||
{
|
||||
// Convert back from MicroUI key modifiers to Raylib keys for checking
|
||||
KeyboardKey raylibKey = KeyboardKey.Null;
|
||||
if (pressedKey == (int)KeyModifiers.Return) raylibKey = KeyboardKey.Enter;
|
||||
if (pressedKey == (int)KeyModifiers.Backspace) raylibKey = KeyboardKey.Backspace;
|
||||
|
||||
if (raylibKey != KeyboardKey.Null && !Raylib.IsKeyDown(raylibKey))
|
||||
{
|
||||
MuInput.KeyUp(ctx, pressedKey);
|
||||
keysToRemove.Add(pressedKey);
|
||||
}
|
||||
}
|
||||
foreach (int keyToRemove in keysToRemove)
|
||||
{
|
||||
pressedKeys.Remove(keyToRemove);
|
||||
}
|
||||
|
||||
// Handle text input
|
||||
int keyChar;
|
||||
while ((keyChar = Raylib.GetCharPressed()) != 0)
|
||||
{
|
||||
char typedChar = (char)keyChar;
|
||||
MuInput.InputText(ctx, typedChar.ToString());
|
||||
}
|
||||
// Debug print for KeyPressed (commented out to reduce noise)
|
||||
// Console.WriteLine($"DEBUG: KeyPressed after input: {ctx.KeyPressed}");
|
||||
MuInput.MouseUp(ctx, (int)mousePos.X, (int)mousePos.Y, (int)MouseButton.Left);
|
||||
|
||||
Raylib.BeginDrawing();
|
||||
Raylib.ClearBackground(Color.Black);
|
||||
Raylib.ClearBackground(Color.DarkGray);
|
||||
|
||||
// Begin frame
|
||||
MicroUI.Begin(ctx);
|
||||
|
||||
for(int i = 0; i < 1; i++){
|
||||
Window(ctx, i * 15, i * 15, i, ref isChecked, ref sliderValue); // Remove ref sliderValue
|
||||
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);
|
||||
}
|
||||
// End frame
|
||||
|
||||
MicroUI.End(ctx);
|
||||
|
||||
|
||||
|
||||
// Print all commands in the command list
|
||||
int cmdIndex = 0;
|
||||
MuCommand? cmd = null;
|
||||
while ((cmd = MuCommandList.NextCommand(ctx, ref cmdIndex)) != null)
|
||||
{
|
||||
//Console.Write($"Command: {cmd.Type}");
|
||||
switch (cmd)
|
||||
{
|
||||
case MuRectCommand rectCmd:
|
||||
//Console.WriteLine($" | Rect: ({rectCmd.Rect.X},{rectCmd.Rect.Y},{rectCmd.Rect.W},{rectCmd.Rect.H}) Color: {rectCmd.Color.R},{rectCmd.Color.G},{rectCmd.Color.B},{rectCmd.Color.A}");
|
||||
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:
|
||||
//Console.Write($" | Clip: ({clipCmd.Rect.X},{clipCmd.Rect.Y},{clipCmd.Rect.W},{clipCmd.Rect.H})");
|
||||
Raylib.BeginScissorMode(clipCmd.Rect.X, clipCmd.Rect.Y, clipCmd.Rect.W, clipCmd.Rect.H);
|
||||
break;
|
||||
case MuJumpCommand jumpCmd:
|
||||
//Console.Write($" | Jump to: {jumpCmd.DestinationIndex}");
|
||||
break;
|
||||
case MuTextCommand textCmd:
|
||||
Raylib.DrawText(textCmd.Text, textCmd.Position.X, textCmd.Position.Y, 10, new Color(textCmd.Color.R, textCmd.Color.G, textCmd.Color.B, textCmd.Color.A));
|
||||
//Console.Write($" | Text: '{textCmd.Text}' at ({textCmd.Position.X},{textCmd.Position.Y}) Color: {textCmd.Color.R},{textCmd.Color.G},{textCmd.Color.B},{textCmd.Color.A}");
|
||||
break;
|
||||
case MuIconCommand iconCmd:
|
||||
if (iconCmd.IconId == (int)IconType.Check)
|
||||
{
|
||||
// Draw a check mark
|
||||
int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2;
|
||||
int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2;
|
||||
int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3;
|
||||
|
||||
// Draw check mark lines
|
||||
Raylib.DrawLine(
|
||||
centerX - size, centerY - size/2,
|
||||
centerX - size/3, centerY + size/2,
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
Raylib.DrawLine(
|
||||
centerX - size/3, centerY + size/2,
|
||||
centerX + size, centerY - size,
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
}
|
||||
else if (iconCmd.IconId == (int)IconType.Close)
|
||||
{
|
||||
// Draw an X for close
|
||||
int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2;
|
||||
int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2;
|
||||
int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3;
|
||||
|
||||
// Draw X lines
|
||||
Raylib.DrawLine(
|
||||
centerX - size, centerY - size,
|
||||
centerX + size, centerY + size,
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
Raylib.DrawLine(
|
||||
centerX - size, centerY + size,
|
||||
centerX + size, centerY - size,
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
}
|
||||
else if (iconCmd.IconId == (int)IconType.Collapsed)
|
||||
{
|
||||
// Draw a right-pointing triangle for collapsed
|
||||
int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2;
|
||||
int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2;
|
||||
int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3;
|
||||
|
||||
// Draw triangle pointing right
|
||||
Raylib.DrawTriangle(
|
||||
new Vector2(centerX - size/2, centerY - size),
|
||||
new Vector2(centerX + size/2, centerY),
|
||||
new Vector2(centerX - size/2, centerY + size),
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
}
|
||||
else if (iconCmd.IconId == (int)IconType.Expanded)
|
||||
{
|
||||
// Draw a down-pointing triangle for expanded
|
||||
int centerX = iconCmd.Rect.X + iconCmd.Rect.W / 2;
|
||||
int centerY = iconCmd.Rect.Y + iconCmd.Rect.H / 2;
|
||||
int size = Math.Min(iconCmd.Rect.W, iconCmd.Rect.H) / 3;
|
||||
|
||||
// Draw triangle pointing down
|
||||
Raylib.DrawTriangle(
|
||||
new Vector2(centerX - size, centerY - size/2),
|
||||
new Vector2(centerX + size, centerY - size/2),
|
||||
new Vector2(centerX, centerY + size/2),
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For unknown icons, draw a colored rectangle
|
||||
Raylib.DrawRectangle(iconCmd.Rect.X, iconCmd.Rect.Y, iconCmd.Rect.W, iconCmd.Rect.H,
|
||||
new Color(iconCmd.Color.R, iconCmd.Color.G, iconCmd.Color.B, iconCmd.Color.A));
|
||||
}
|
||||
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();
|
||||
//break;
|
||||
}
|
||||
|
||||
Raylib.CloseWindow();
|
||||
}
|
||||
|
||||
|
||||
public static void Window(MuContext ctx, int x, int y, int index, ref bool isChecked, ref float sliderValue){
|
||||
if(MuControl.BeginWindowEx(ctx, $"Test Window", new MuRect(x, y, 300, 200), 0)){
|
||||
// Manual two-column layout
|
||||
MuLayoutUtil.LayoutRow(ctx, 2, new int[] { 120, 160 }, 0); // Adjust widths as needed
|
||||
|
||||
|
||||
// Left column (sliders/text)
|
||||
private static void HandleKey(string key)
|
||||
{
|
||||
if (key == "<-")
|
||||
{
|
||||
if (evaluated)
|
||||
{
|
||||
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 120 }, 0);
|
||||
float test = 0;
|
||||
//var sliderResult = MuControl.SliderEx(ctx, "main_slider", ref sliderValue, 0.0f, 100.0f, 1.0f, "F1", 0);
|
||||
MuControl.SliderEx(ctx, "test_slider", ref test, 0.0f, 100.0f, 1.0f, "F1", 0);
|
||||
// if ((sliderResult & ResultFlags.Change) != 0)
|
||||
// {
|
||||
// Console.WriteLine($"Slider value changed to: {sliderValue}");
|
||||
// }
|
||||
MuControl.Text(ctx, "Other controls here...");
|
||||
display = "0";
|
||||
currentInput = "";
|
||||
expression = "";
|
||||
evaluated = false;
|
||||
}
|
||||
else if (expression.Length > 0)
|
||||
{
|
||||
expression = expression[..^1];
|
||||
if (currentInput.Length > 0)
|
||||
currentInput = currentInput[..^1];
|
||||
if (expression.Length == 0)
|
||||
display = "0";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Test TextboxRaw
|
||||
var textboxRect = MuLayoutUtil.LayoutNext(ctx);
|
||||
var textboxResult = MuControl.TextboxRaw(ctx, ref textboxValue, 12345, textboxRect, 0);
|
||||
if ((textboxResult & ResultFlags.Change) != 0)
|
||||
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
|
||||
{
|
||||
Console.WriteLine($"Textbox changed: {textboxValue}");
|
||||
var result = EvaluateExpression(expression);
|
||||
display = FormatResult(result);
|
||||
lastResult = result;
|
||||
lastOperation = expression;
|
||||
currentInput = "";
|
||||
expression = "";
|
||||
evaluated = true;
|
||||
}
|
||||
|
||||
// Test NumberEx (number textbox)
|
||||
MuControl.Text(ctx, "Number control (Shift+Click to edit):");
|
||||
var numberResult = MuControl.NumberEx(ctx, "test_number", ref sliderValue, 1.0f, "F2", 0);
|
||||
if ((numberResult & ResultFlags.Change) != 0)
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Number changed to: {sliderValue}");
|
||||
}
|
||||
|
||||
// Test second NumberEx (number textbox)
|
||||
MuControl.Text(ctx, "Second number control (Shift+Click to edit):");
|
||||
var secondNumberResult = MuControl.NumberEx(ctx, "test_number2", ref secondValue, 0.5f, "F2", 0);
|
||||
if ((secondNumberResult & ResultFlags.Change) != 0)
|
||||
{
|
||||
Console.WriteLine($"Second number changed to: {secondValue}");
|
||||
display = "Error";
|
||||
}
|
||||
}
|
||||
|
||||
// Right column (button grid)
|
||||
else if (evaluated && lastOperation.Length > 0)
|
||||
{
|
||||
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 160 }, 0);
|
||||
MuControl.Text(ctx, "4x4 Grid of Buttons with 'x' labels:");
|
||||
for (int row = 0; row < 4; row++)
|
||||
try
|
||||
{
|
||||
MuLayoutUtil.LayoutRow(ctx, 4, new int[] { 25, 25, 25, 25 }, 0);
|
||||
for (int col = 0; col < 4; col++)
|
||||
var tokens = Tokenize(lastOperation);
|
||||
if (tokens.Count == 3 && tokens[1] is "+" or "-" or "*" or "/")
|
||||
{
|
||||
MicroUI.PushId(ctx, System.Text.Encoding.UTF8.GetBytes($"grid_{row}_{col}"));
|
||||
if (MuControl.ButtonEx(ctx, "x", 0, (int)Options.AlignCenter))
|
||||
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--)
|
||||
{
|
||||
Console.WriteLine($"Button at ({row},{col}) clicked!");
|
||||
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;
|
||||
}
|
||||
MicroUI.PopId(ctx);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
display = "Error";
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (key is "(" or ")")
|
||||
{
|
||||
if (evaluated)
|
||||
{
|
||||
expression = "";
|
||||
evaluated = false;
|
||||
}
|
||||
expression += key;
|
||||
currentInput = "";
|
||||
return;
|
||||
}
|
||||
|
||||
MuControl.EndWindow(ctx);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user