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 string Version = "1.0.0";
|
||||||
public const int CommandListSize = 256 * 1024;
|
public const int CommandListSize = 256 * 1024;
|
||||||
public const int RootListSize = 32;
|
public const int RootListSize = 256;
|
||||||
public const int ContainerStackSize = 32;
|
public const int ContainerStackSize = 256;
|
||||||
public const int ClipStackSize = 32;
|
public const int ClipStackSize = 256;
|
||||||
public const int IdStackSize = 32;
|
public const int IdStackSize = 256;
|
||||||
public const int LayoutStackSize = 16;
|
public const int LayoutStackSize = 256;
|
||||||
public const int ContainerPoolSize = 48;
|
public const int ContainerPoolSize = 256;
|
||||||
public const int TreeNodePoolSize = 48;
|
public const int TreeNodePoolSize = 256;
|
||||||
public const int MaxWidths = 16;
|
public const int MaxWidths = 256;
|
||||||
public const int MaxFormatLength = 127;
|
public const int MaxFormatLength = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Format
|
public static class Format
|
||||||
|
|||||||
@ -1,324 +1,347 @@
|
|||||||
using Raylib_cs;
|
using Raylib_cs;
|
||||||
using MicroUI;
|
using MicroUI;
|
||||||
using System.Numerics;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MicroUI;
|
namespace MicroUI;
|
||||||
|
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
// Key state tracking for key up events
|
private static string display = "0";
|
||||||
private static HashSet<int> pressedKeys = new HashSet<int>();
|
private static string expression = "";
|
||||||
// Persistent textbox value
|
private static string lastOperation = "";
|
||||||
private static string textboxValue = "test";
|
private static double lastResult = 0;
|
||||||
// Second number value for testing multiple number controls
|
private static bool evaluated = false;
|
||||||
private static float secondValue = 25.0f;
|
private static string currentInput = "";
|
||||||
// Track focus changes
|
|
||||||
private static uint lastFocus = 0;
|
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]
|
[STAThread]
|
||||||
public static void Main()
|
public static void Main()
|
||||||
{
|
{
|
||||||
Raylib.InitWindow(800, 480, "Hello World");
|
const int winW = 300;
|
||||||
|
const int winH = 450;
|
||||||
// Create microui context
|
Raylib.SetConfigFlags(ConfigFlags.ResizableWindow);
|
||||||
|
Raylib.InitWindow(800, 600, "Calculator");
|
||||||
var ctx = new MuContext();
|
var ctx = new MuContext();
|
||||||
|
ctx.TextWidth = (object font, string str, int len) => Raylib.MeasureText(str, 20);
|
||||||
// Set up required callbacks
|
ctx.TextHeight = (object font) => 24;
|
||||||
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);
|
MicroUI.Init(ctx);
|
||||||
|
|
||||||
bool isChecked = false;
|
|
||||||
float sliderValue = 50.0f; // Remove persistent slider value
|
|
||||||
|
|
||||||
|
|
||||||
while (!Raylib.WindowShouldClose())
|
while (!Raylib.WindowShouldClose())
|
||||||
{
|
{
|
||||||
// Handle input
|
|
||||||
var mousePos = Raylib.GetMousePosition();
|
var mousePos = Raylib.GetMousePosition();
|
||||||
MuInput.MouseMove(ctx, (int)mousePos.X, (int)mousePos.Y);
|
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))
|
if (Raylib.IsMouseButtonPressed(Raylib_cs.MouseButton.Left))
|
||||||
{
|
MuInput.MouseDown(ctx, (int)mousePos.X, (int)mousePos.Y, (int)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;
|
|
||||||
}
|
|
||||||
if (Raylib.IsMouseButtonReleased(Raylib_cs.MouseButton.Left))
|
if (Raylib.IsMouseButtonReleased(Raylib_cs.MouseButton.Left))
|
||||||
{
|
MuInput.MouseUp(ctx, (int)mousePos.X, (int)mousePos.Y, (int)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}");
|
|
||||||
|
|
||||||
Raylib.BeginDrawing();
|
Raylib.BeginDrawing();
|
||||||
Raylib.ClearBackground(Color.Black);
|
Raylib.ClearBackground(Color.DarkGray);
|
||||||
|
|
||||||
// Begin frame
|
|
||||||
MicroUI.Begin(ctx);
|
MicroUI.Begin(ctx);
|
||||||
|
|
||||||
for(int i = 0; i < 1; i++){
|
var winX = (Raylib.GetScreenWidth() - winW) / 2;
|
||||||
Window(ctx, i * 15, i * 15, i, ref isChecked, ref sliderValue); // Remove ref sliderValue
|
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);
|
||||||
}
|
}
|
||||||
// End frame
|
}
|
||||||
|
|
||||||
|
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);
|
MicroUI.End(ctx);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Print all commands in the command list
|
|
||||||
int cmdIndex = 0;
|
int cmdIndex = 0;
|
||||||
MuCommand? cmd = null;
|
MuCommand? cmd = null;
|
||||||
while ((cmd = MuCommandList.NextCommand(ctx, ref cmdIndex)) != null)
|
while ((cmd = MuCommandList.NextCommand(ctx, ref cmdIndex)) != null)
|
||||||
{
|
{
|
||||||
//Console.Write($"Command: {cmd.Type}");
|
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
case MuRectCommand rectCmd:
|
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));
|
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;
|
break;
|
||||||
case MuClipCommand clipCmd:
|
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);
|
Raylib.BeginScissorMode(clipCmd.Rect.X, clipCmd.Rect.Y, clipCmd.Rect.W, clipCmd.Rect.H);
|
||||||
break;
|
break;
|
||||||
case MuJumpCommand jumpCmd:
|
|
||||||
//Console.Write($" | Jump to: {jumpCmd.DestinationIndex}");
|
|
||||||
break;
|
|
||||||
case MuTextCommand textCmd:
|
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));
|
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));
|
||||||
//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));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Raylib.EndDrawing();
|
Raylib.EndDrawing();
|
||||||
//break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Raylib.CloseWindow();
|
Raylib.CloseWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void HandleKey(string key)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 120 }, 0);
|
if (key == "<-")
|
||||||
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...");
|
|
||||||
|
|
||||||
// Test TextboxRaw
|
|
||||||
var textboxRect = MuLayoutUtil.LayoutNext(ctx);
|
|
||||||
var textboxResult = MuControl.TextboxRaw(ctx, ref textboxValue, 12345, textboxRect, 0);
|
|
||||||
if ((textboxResult & ResultFlags.Change) != 0)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Textbox changed: {textboxValue}");
|
if (evaluated)
|
||||||
|
{
|
||||||
|
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 NumberEx (number textbox)
|
if (char.IsDigit(key, 0) || key == ".")
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Number changed to: {sliderValue}");
|
if (evaluated)
|
||||||
|
{
|
||||||
|
expression = "";
|
||||||
|
evaluated = false;
|
||||||
|
}
|
||||||
|
if (key == "." && currentInput.Contains(".")) return;
|
||||||
|
currentInput += key;
|
||||||
|
expression += key;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test second NumberEx (number textbox)
|
if (key == "C")
|
||||||
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 = "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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right column (button grid)
|
private static bool IsOperator(char c) => c is '+' or '-' or '*' or '/';
|
||||||
|
|
||||||
|
private static double EvaluateExpression(string expr)
|
||||||
{
|
{
|
||||||
MuLayoutUtil.LayoutRow(ctx, 1, new int[] { 160 }, 0);
|
var tokens = Tokenize(expr);
|
||||||
MuControl.Text(ctx, "4x4 Grid of Buttons with 'x' labels:");
|
int idx = 0;
|
||||||
for (int row = 0; row < 4; row++)
|
var result = ParseExpression(tokens, ref idx);
|
||||||
{
|
if (idx < tokens.Count)
|
||||||
MuLayoutUtil.LayoutRow(ctx, 4, new int[] { 25, 25, 25, 25 }, 0);
|
throw new Exception("Unexpected token: " + tokens[idx]);
|
||||||
for (int col = 0; col < 4; col++)
|
return result;
|
||||||
{
|
|
||||||
MicroUI.PushId(ctx, System.Text.Encoding.UTF8.GetBytes($"grid_{row}_{col}"));
|
|
||||||
if (MuControl.ButtonEx(ctx, "x", 0, (int)Options.AlignCenter))
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Button at ({row},{col}) clicked!");
|
|
||||||
}
|
|
||||||
MicroUI.PopId(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
MuControl.EndWindow(ctx);
|
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