diff --git a/.gitignore b/.gitignore
index c1b0839..484a9dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ C/microui_test
.DS_Store
Thumbs.db
.vscode/
+
+build/
\ No newline at end of file
diff --git a/MicroUI.cs/MicroUI.cs b/MicroUI.cs/MicroUI.cs
index f7e7c94..47bb042 100644
--- a/MicroUI.cs/MicroUI.cs
+++ b/MicroUI.cs/MicroUI.cs
@@ -616,7 +616,7 @@ namespace MicroUI
}
ctx.KeyPressed = 0;
- ctx.inputText = new char[32];
+ ctx.inputText[0] = '\0';
ctx.MousePressed = 0;
ctx.ScrollDelta = new MuVec2(0, 0);
ctx.LastMousePos = ctx.MousePos;
@@ -904,15 +904,21 @@ namespace MicroUI
public static void InputText(MuContext ctx, string text)
{
- int len = ctx.inputText.Length;
+ // Find the current length of text in the buffer (up to first null terminator)
+ int len = 0;
+ while (len < ctx.inputText.Length && ctx.inputText[len] != '\0')
+ len++;
+
int size = text.Length;
- if (len + size > ctx.inputText.Length)
+ if (len + size + 1 > ctx.inputText.Length) // +1 for null terminator
throw new Exception("Input text buffer overflow");
- // Copy text into the buffer (simple version)
- for (int i = 0; i < size && i < ctx.inputText.Length; i++)
- {
- ctx.inputText[i] = text[i];
- }
+
+ // Copy text into the buffer at the current position
+ for (int i = 0; i < size && (len + i) < ctx.inputText.Length - 1; i++)
+ ctx.inputText[len + i] = text[i];
+
+ // Null-terminate
+ ctx.inputText[len + size] = '\0';
}
}
@@ -1275,6 +1281,16 @@ namespace MicroUI
uint resizeId = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes("!resize"));
var r = new MuRect(rect.X + rect.W - sz, rect.Y + rect.H - sz, sz, sz);
MuControl.UpdateControl(ctx, resizeId, r, opt);
+ // Chevron/arrow design for resize handle
+ int pad = 4;
+ var baseColor = ctx.Style.Colors[(int)ColorType.Base];
+ // Horizontal bar (bottom edge)
+ MuCommandList.Push(ctx, new MuRectCommand(
+ new MuRect(r.X + r.W - pad - 8, r.Y + r.H - pad - 2, 8, 2), baseColor));
+
+ // Vertical bar (right edge)
+ MuCommandList.Push(ctx, new MuRectCommand(
+ new MuRect(r.X + r.W - pad - 2, r.Y + r.H - pad - 8, 2, 8), baseColor));
if (resizeId == ctx.Focus && (ctx.MouseDown & (int)MouseButton.Left) != 0)
{
cnt.Rect = new MuRect(
@@ -1487,7 +1503,7 @@ namespace MicroUI
return new MuResult(res);
}
-
+
public static void Text(MuContext ctx, string text)
{
if (ctx.TextWidth == null || ctx.TextHeight == null) return;
@@ -1529,5 +1545,192 @@ namespace MicroUI
MuRect r = MuLayoutUtil.LayoutNext(ctx);
DrawControlText(ctx, text, r, ColorType.Text, 0);
}
+
+ // Helper method for number textbox functionality
+ private static bool NumberTextbox(MuContext ctx, ref float value, MuRect r, uint id)
+ {
+ // Check if we should enter text input mode (Shift+Click)
+ if ((ctx.MousePressed & (int)MouseButton.Left) != 0 &&
+ (ctx.KeyDown & (int)KeyModifiers.Shift) != 0 &&
+ ctx.Hover == id)
+ {
+ ctx.NumberEdit = id;
+ // Convert number to string for editing using the same format as C version
+ var numberStr = value.ToString("G3"); // MU_REAL_FMT is "%.3g"
+ // Copy to input buffer (simplified - in real implementation you'd need a proper buffer)
+ for (int i = 0; i < Math.Min(numberStr.Length, ctx.inputText.Length); i++)
+ {
+ ctx.inputText[i] = numberStr[i];
+ }
+ }
+
+ if (ctx.NumberEdit == id)
+ {
+ // For now, we'll implement a simplified textbox
+ // In a full implementation, you'd use TextboxRaw here
+ var result = TextboxRaw(ctx, ctx.inputText, ctx.inputText.Length, id, r, 0);
+ if ((result & ResultFlags.Submit) != 0 || ctx.Focus != id)
+ {
+ // Parse the number back
+ if (float.TryParse(new string(ctx.inputText).Trim('\0'), out float newValue))
+ {
+ value = newValue;
+ }
+ ctx.NumberEdit = 0;
+ }
+ else
+ {
+ return true; // Still in text input mode
+ }
+ }
+ return false;
+ }
+
+ public static MuResult TextboxRaw(MuContext ctx, char[] buf, int bufSize, uint id, MuRect r, int opt)
+ {
+ ResultFlags res = 0;
+ UpdateControl(ctx, id, r, opt | (int)Options.HoldFocus);
+
+ if (ctx.Focus == id)
+ {
+ // Handle text input
+ int len = 0;
+ while (len < bufSize && buf[len] != '\0') len++;
+
+ // Get actual length of text in input buffer (up to first null terminator)
+ int inputLen = 0;
+ while (inputLen < ctx.inputText.Length && ctx.inputText[inputLen] != '\0') inputLen++;
+
+ int n = Math.Min(bufSize - len - 1, inputLen);
+ if (n > 0)
+ {
+ for (int i = 0; i < n; i++)
+ {
+ if (len + i < bufSize)
+ buf[len + i] = ctx.inputText[i];
+ }
+ len += n;
+ if (len < bufSize) buf[len] = '\0';
+ res |= ResultFlags.Change;
+ }
+
+ // Handle backspace
+ if ((ctx.KeyPressed & (int)KeyModifiers.Backspace) != 0 && len > 0)
+ {
+ // Skip UTF-8 continuation bytes (simplified for C#)
+ while (len > 0 && (buf[len - 1] & 0xC0) == 0x80) len--;
+ if (len > 0) len--;
+ buf[len] = '\0';
+ res |= ResultFlags.Change;
+ }
+
+ // Handle return
+ if ((ctx.KeyPressed & (int)KeyModifiers.Return) != 0)
+ {
+ MicroUI.SetFocus(ctx, 0);
+ res |= ResultFlags.Submit;
+ }
+ }
+
+ // Draw
+ DrawControlFrame(ctx, id, r, ColorType.Base, opt);
+ string displayText = new string(buf).Trim('\0');
+ DrawControlText(ctx, displayText, r, ColorType.Text, opt);
+
+ return new MuResult(res);
+ }
+
+ public static MuResult SliderEx(MuContext ctx, string name, ref float value, float low, float high, float step, string format, int opt)
+ {
+ char[] buf = new char[Constants.MaxFormatLength + 1];
+ MuRect thumb;
+ int x, w;
+ ResultFlags res = 0;
+ float last = value, v = last;
+
+ // Generate ID from the name, just like BeginWindowEx does
+ uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name));
+
+ MuRect baseRect = MuLayoutUtil.LayoutNext(ctx);
+
+ // Handle text input mode
+ if (NumberTextbox(ctx, ref v, baseRect, id))
+ {
+ return new MuResult(res);
+ }
+
+ // Handle normal mode
+ UpdateControl(ctx, id, baseRect, opt);
+
+ // Handle input - EXACTLY match C version logic
+ if (ctx.Focus == id && (ctx.MouseDown | ctx.MousePressed) == (int)MouseButton.Left)
+ {
+ v = low + (ctx.MousePos.X - baseRect.X) * (high - low) / baseRect.W;
+ if (step != 0)
+ {
+ v = (long)((v + step / 2) / step) * step;
+ }
+ }
+
+ // Clamp and store value, update res
+ value = v = MathUtil.Clamp(v, low, high);
+ if (last != v) { res |= ResultFlags.Change; }
+
+ // Draw base
+ DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt);
+
+ // Draw thumb
+ w = ctx.Style.ThumbSize;
+ x = (int)((v - low) * (baseRect.W - w) / (high - low));
+ thumb = new MuRect(baseRect.X + x, baseRect.Y, w, baseRect.H);
+ DrawControlFrame(ctx, id, thumb, ColorType.Button, opt);
+
+ // Draw text
+ string text = v.ToString(format);
+ DrawControlText(ctx, text, baseRect, ColorType.Text, opt);
+
+ return new MuResult(res);
+ }
+
+ // Remove the caller information overload since we're using explicit names now
+
+ public static MuResult NumberEx(MuContext ctx, string name, ref float value, float step, string format, int opt)
+ {
+ char[] buf = new char[Constants.MaxFormatLength + 1];
+ ResultFlags res = 0;
+
+ // Generate ID from the name, just like BeginWindowEx does
+ uint id = MicroUI.GetId(ctx, System.Text.Encoding.UTF8.GetBytes(name));
+
+ MuRect baseRect = MuLayoutUtil.LayoutNext(ctx);
+ float last = value;
+
+ // Handle text input mode
+ if (NumberTextbox(ctx, ref value, baseRect, id))
+ {
+ return new MuResult(res);
+ }
+
+ // Handle normal mode
+ UpdateControl(ctx, id, baseRect, opt);
+
+ // Handle input - EXACTLY match C version logic
+ if (ctx.Focus == id && ctx.MouseDown == (int)MouseButton.Left)
+ {
+ value += ctx.MouseDelta.X * step;
+ }
+
+ // Set flag if value changed
+ if (value != last) { res |= ResultFlags.Change; }
+
+ // Draw base
+ DrawControlFrame(ctx, id, baseRect, ColorType.Base, opt);
+
+ // Draw text
+ string text = value.ToString(format);
+ DrawControlText(ctx, text, baseRect, ColorType.Text, opt);
+
+ return new MuResult(res);
+ }
}
}
\ No newline at end of file
diff --git a/MicroUI.cs/MicroUI.cs.csproj b/MicroUI.cs/MicroUI.cs.csproj
index b31bb32..e6b18b1 100644
--- a/MicroUI.cs/MicroUI.cs.csproj
+++ b/MicroUI.cs/MicroUI.cs.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net9.0
enable
enable
diff --git a/MicroUI.cs/Program.cs b/MicroUI.cs/Program.cs
index 177d9b8..2de702e 100644
--- a/MicroUI.cs/Program.cs
+++ b/MicroUI.cs/Program.cs
@@ -6,6 +6,14 @@ namespace MicroUI;
class Program
{
+ // Key state tracking for key up events
+ private static HashSet pressedKeys = new HashSet();
+ // Persistent textbox buffer
+ private static char[] textboxBuf = new char[32];
+ private static bool textboxBufInitialized = false;
+ // Track focus changes
+ private static uint lastFocus = 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()
@@ -18,17 +26,23 @@ class Program
// Set up required callbacks
ctx.TextWidth = (object font, string str, int len) =>
{
- return str.Length * 8; // Simple approximation
+ // 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) =>
{
- return 16; // Simple approximation
+ // 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
while (!Raylib.WindowShouldClose())
@@ -37,28 +51,88 @@ class Program
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))
{
- Console.WriteLine("Mouse down");
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))
{
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 key up events for modifier keys
+ var keysToRemove = new List();
+ 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.ClearBackground(Color.Black);
// Begin frame
MicroUI.Begin(ctx);
- for(int i = 0; i < 10; i++){
- Window(ctx, i * 15, i * 15, i, ref isChecked);
+ for(int i = 0; i < 1; i++){
+ Window(ctx, i * 15, i * 15, i, ref isChecked, ref sliderValue); // Remove ref sliderValue
}
// End frame
MicroUI.End(ctx);
+
+
// Print all commands in the command list
int cmdIndex = 0;
MuCommand? cmd = null;
@@ -79,7 +153,7 @@ class Program
//Console.Write($" | Jump to: {jumpCmd.DestinationIndex}");
break;
case MuTextCommand textCmd:
- Raylib.DrawText(textCmd.Text, textCmd.Position.X, textCmd.Position.Y, 16, new Color(textCmd.Color.R, textCmd.Color.G, textCmd.Color.B, textCmd.Color.A));
+ 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:
@@ -170,47 +244,60 @@ class Program
}
- public static void Window(MuContext ctx, int x, int y, int index, ref bool isChecked){
- if(MuControl.BeginWindowEx(ctx, $"Test Window {index}", new MuRect(x, y, 300, 200), 0)){
-
- MuLayoutUtil.LayoutBeginColumn(ctx);
-
- // Test Text component with multi-line content
- MuControl.Text(ctx, "This is a test of the Text component with word wrapping. It should automatically wrap long lines and handle multiple paragraphs.");
-
- // Test Label component
- // MuControl.Label(ctx, "This is a simple label");
-
- // Test button
- // if (MuControl.ButtonEx(ctx, "Click Me!", 0, 0))
- // {
- // Console.WriteLine("Button clicked!");
- // }
- // MuLayoutUtil.LayoutRow(ctx, 2, new int[]{200, 50}, 0);
- // if(MuControl.Checkbox(ctx, "Checkbox1", ref isChecked)){
- // Console.WriteLine("Checkbox clicked!");
- // }
- // if(MuControl.ButtonEx(ctx, null, (int)IconType.Check, 0)){
- // Console.WriteLine("Button clicked!");
- // }
-
- // Test more text with explicit line breaks
- //MuControl.Text(ctx, "This text has\nexplicit line breaks\nand should display\non separate lines.");
-
- // if(MuControl.Checkbox(ctx, "Checkbox2", ref isChecked)){
- // Console.WriteLine("Checkbox clicked!");
- // }
- // if(MuControl.Checkbox(ctx, "Checkbox3", ref isChecked)){
- // Console.WriteLine("Checkbox clicked!");
- // }
- // if(MuControl.Checkbox(ctx, "Checkbox4", ref isChecked)){
- // Console.WriteLine("Checkbox clicked!");
- // }
-
- MuLayoutUtil.LayoutEndColumn(ctx);
- MuControl.EndWindow(ctx);
- }
+ 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);
+ 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
+ if (!textboxBufInitialized) {
+ string initial = "Edit me!";
+ for (int i = 0; i < initial.Length && i < textboxBuf.Length; i++) textboxBuf[i] = initial[i];
+ textboxBufInitialized = true;
+ }
+ var textboxRect = MuLayoutUtil.LayoutNext(ctx);
+ var textboxResult = MuControl.TextboxRaw(ctx, textboxBuf, textboxBuf.Length, 12345, textboxRect, 0);
+ if ((textboxResult & ResultFlags.Change) != 0)
+ {
+ string value = new string(textboxBuf).TrimEnd('\0');
+ Console.WriteLine($"Textbox changed: {value}");
+ }
+ }
+
+ // Right column (button grid)
+ {
+ 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++)
+ {
+ MuLayoutUtil.LayoutRow(ctx, 4, new int[] { 25, 25, 25, 25 }, 0);
+ for (int col = 0; col < 4; col++)
+ {
+ 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);
+ }
+ }
+ }
+
+
+ MuControl.EndWindow(ctx);
+ }
}
}
\ No newline at end of file
diff --git a/MicroUICVersion/CMakeLists.txt b/MicroUICVersion/CMakeLists.txt
new file mode 100644
index 0000000..06acce8
--- /dev/null
+++ b/MicroUICVersion/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.11) # FetchContent is available in 3.11+
+project(example)
+
+# Generate compile_commands.json
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# Dependencies
+set(RAYLIB_VERSION 5.5)
+find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED
+if (NOT raylib_FOUND) # If there's none, fetch and build raylib
+ include(FetchContent)
+ FetchContent_Declare(
+ raylib
+ DOWNLOAD_EXTRACT_TIMESTAMP OFF
+ URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz
+ )
+ FetchContent_GetProperties(raylib)
+ if (NOT raylib_POPULATED) # Have we downloaded raylib yet?
+ set(FETCHCONTENT_QUIET NO)
+ FetchContent_MakeAvailable(raylib)
+ endif()
+endif()
+
+# Our Project
+
+add_executable(${PROJECT_NAME} main.c
+ microui.c)
+#set(raylib_VERBOSE 1)
+target_link_libraries(${PROJECT_NAME} raylib)
+
+# Web Configurations
+if (${PLATFORM} STREQUAL "Web")
+ set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".html") # Tell Emscripten to build an example.html file.
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_GLFW=3 -s ASSERTIONS=1 -s WASM=1 -s ASYNCIFY -s GL_ENABLE_GET_PROC_ADDRESS=1")
+endif()
+
+# Checks if OSX and links appropriate frameworks (Only required on MacOS)
+if (APPLE)
+ target_link_libraries(${PROJECT_NAME} "-framework IOKit")
+ target_link_libraries(${PROJECT_NAME} "-framework Cocoa")
+ target_link_libraries(${PROJECT_NAME} "-framework OpenGL")
+endif()
\ No newline at end of file
diff --git a/MicroUICVersion/cmake_install.cmake b/MicroUICVersion/cmake_install.cmake
new file mode 100644
index 0000000..1fe2775
--- /dev/null
+++ b/MicroUICVersion/cmake_install.cmake
@@ -0,0 +1,67 @@
+# Install script for directory: /Users/bobbylucero/CLionProjects/MicroUITest
+
+# Set the install prefix
+if(NOT DEFINED CMAKE_INSTALL_PREFIX)
+ set(CMAKE_INSTALL_PREFIX "/usr/local")
+endif()
+string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
+
+# Set the install configuration name.
+if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
+ if(BUILD_TYPE)
+ string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
+ CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
+ else()
+ set(CMAKE_INSTALL_CONFIG_NAME "")
+ endif()
+ message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
+endif()
+
+# Set the component getting installed.
+if(NOT CMAKE_INSTALL_COMPONENT)
+ if(COMPONENT)
+ message(STATUS "Install component: \"${COMPONENT}\"")
+ set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
+ else()
+ set(CMAKE_INSTALL_COMPONENT)
+ endif()
+endif()
+
+# Is this installation the result of a crosscompile?
+if(NOT DEFINED CMAKE_CROSSCOMPILING)
+ set(CMAKE_CROSSCOMPILING "FALSE")
+endif()
+
+# Set path to fallback-tool for dependency-resolution.
+if(NOT DEFINED CMAKE_OBJDUMP)
+ set(CMAKE_OBJDUMP "/usr/bin/objdump")
+endif()
+
+if(NOT CMAKE_INSTALL_LOCAL_ONLY)
+ # Include the install script for each subdirectory.
+ include("/Users/bobbylucero/CLionProjects/MicroUITest/_deps/raylib-build/cmake_install.cmake")
+
+endif()
+
+string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
+ "${CMAKE_INSTALL_MANIFEST_FILES}")
+if(CMAKE_INSTALL_LOCAL_ONLY)
+ file(WRITE "/Users/bobbylucero/CLionProjects/MicroUITest/install_local_manifest.txt"
+ "${CMAKE_INSTALL_MANIFEST_CONTENT}")
+endif()
+if(CMAKE_INSTALL_COMPONENT)
+ if(CMAKE_INSTALL_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$")
+ set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
+ else()
+ string(MD5 CMAKE_INST_COMP_HASH "${CMAKE_INSTALL_COMPONENT}")
+ set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INST_COMP_HASH}.txt")
+ unset(CMAKE_INST_COMP_HASH)
+ endif()
+else()
+ set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
+endif()
+
+if(NOT CMAKE_INSTALL_LOCAL_ONLY)
+ file(WRITE "/Users/bobbylucero/CLionProjects/MicroUITest/${CMAKE_INSTALL_MANIFEST}"
+ "${CMAKE_INSTALL_MANIFEST_CONTENT}")
+endif()
diff --git a/MicroUICVersion/main.c b/MicroUICVersion/main.c
new file mode 100644
index 0000000..2e7b6ff
--- /dev/null
+++ b/MicroUICVersion/main.c
@@ -0,0 +1,385 @@
+#include
+#include
+#include
+#include
+
+#include "microui.h"
+
+static char logbuf[64000];
+static int logbuf_updated = 0;
+static float bg[3] = { 90, 95, 100 };
+
+
+static float global_scale = 1;
+
+static int font_size = 10;
+
+
+static void write_log(const char *text) {
+ if (logbuf[0]) { strcat(logbuf, "\n"); }
+ strcat(logbuf, text);
+ logbuf_updated = 1;
+}
+
+
+static void test_window(mu_Context *ctx) {
+ /* do window */
+ if (mu_begin_window(ctx, "Demo Window", mu_rect(40, 40, 300, 450))) {
+ mu_Container *win = mu_get_current_container(ctx);
+ win->rect.w = mu_max(win->rect.w, 240);
+ win->rect.h = mu_max(win->rect.h, 300);
+
+ /* window info */
+ if (mu_header(ctx, "Window Info")) {
+ mu_Container *win = mu_get_current_container(ctx);
+ char buf[64];
+ mu_layout_row(ctx, 2, (int[]) { 54, -1 }, 0);
+ mu_label(ctx,"Position:");
+ sprintf(buf, "%d, %d", win->rect.x, win->rect.y); mu_label(ctx, buf);
+ mu_label(ctx, "Size:");
+ sprintf(buf, "%d, %d", win->rect.w, win->rect.h); mu_label(ctx, buf);
+ }
+
+ /* labels + buttons */
+ if (mu_header_ex(ctx, "Test Buttons", MU_OPT_EXPANDED)) {
+ mu_layout_row(ctx, 3, (int[]) { 86, -110, -1 }, 0);
+ mu_label(ctx, "Test buttons 1:");
+ if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); }
+ if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); }
+ mu_label(ctx, "Test buttons 2:");
+ if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); }
+ if (mu_button(ctx, "Popup")) { mu_open_popup(ctx, "Test Popup"); }
+ if (mu_begin_popup(ctx, "Test Popup")) {
+ mu_button(ctx, "Hello");
+ mu_button(ctx, "World");
+ mu_end_popup(ctx);
+ }
+ }
+
+ /* tree */
+ if (mu_header_ex(ctx, "Tree and Text", MU_OPT_EXPANDED)) {
+ mu_layout_row(ctx, 2, (int[]) { 140, -1 }, 0);
+ mu_layout_begin_column(ctx);
+ if (mu_begin_treenode(ctx, "Test 1")) {
+ if (mu_begin_treenode(ctx, "Test 1a")) {
+ mu_label(ctx, "Hello");
+ mu_label(ctx, "world");
+ mu_end_treenode(ctx);
+ }
+ if (mu_begin_treenode(ctx, "Test 1b")) {
+ if (mu_button(ctx, "Button 1")) { write_log("Pressed button 1"); }
+ if (mu_button(ctx, "Button 2")) { write_log("Pressed button 2"); }
+ mu_end_treenode(ctx);
+ }
+ mu_end_treenode(ctx);
+ }
+ if (mu_begin_treenode(ctx, "Test 2")) {
+ mu_layout_row(ctx, 2, (int[]) { 54, 54 }, 0);
+ if (mu_button(ctx, "Button 3")) { write_log("Pressed button 3"); }
+ if (mu_button(ctx, "Button 4")) { write_log("Pressed button 4"); }
+ if (mu_button(ctx, "Button 5")) { write_log("Pressed button 5"); }
+ if (mu_button(ctx, "Button 6")) { write_log("Pressed button 6"); }
+ mu_end_treenode(ctx);
+ }
+ if (mu_begin_treenode(ctx, "Test 3")) {
+ static int checks[3] = { 1, 0, 1 };
+ mu_checkbox(ctx, "Checkbox 1", &checks[0]);
+ mu_checkbox(ctx, "Checkbox 2", &checks[1]);
+ mu_checkbox(ctx, "Checkbox 3", &checks[2]);
+ mu_end_treenode(ctx);
+ }
+ mu_layout_end_column(ctx);
+
+ mu_layout_begin_column(ctx);
+ mu_layout_row(ctx, 1, (int[]) { -1 }, 0);
+ mu_text(ctx, "Lorem ipsum dolor sit amet, consectetur adipiscing "
+ "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus "
+ "ipsum, eu varius magna felis a nulla.");
+ mu_layout_end_column(ctx);
+ }
+
+ /* background color sliders */
+ if (mu_header_ex(ctx, "Background Color", MU_OPT_EXPANDED)) {
+ mu_layout_row(ctx, 2, (int[]) { -78, -1 }, 74);
+ /* sliders */
+ mu_layout_begin_column(ctx);
+ mu_layout_row(ctx, 2, (int[]) { 46, -1 }, 0);
+ mu_label(ctx, "Red:"); mu_slider(ctx, &bg[0], 0, 255);
+ mu_label(ctx, "Green:"); mu_slider(ctx, &bg[1], 0, 255);
+ mu_label(ctx, "Blue:"); mu_slider(ctx, &bg[2], 0, 255);
+ mu_layout_end_column(ctx);
+ /* color preview */
+ mu_Rect r = mu_layout_next(ctx);
+ mu_draw_rect(ctx, r, mu_color(bg[0], bg[1], bg[2], 255));
+ char buf[32];
+ sprintf(buf, "#%02X%02X%02X", (int) bg[0], (int) bg[1], (int) bg[2]);
+ mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, MU_OPT_ALIGNCENTER);
+ }
+
+ mu_end_window(ctx);
+ }
+}
+
+
+static void log_window(mu_Context *ctx) {
+ if (mu_begin_window(ctx, "Log Window", mu_rect(350, 40, 300, 200))) {
+ /* output text panel */
+ mu_layout_row(ctx, 1, (int[]) { -1 }, -25);
+ mu_begin_panel(ctx, "Log Output");
+ mu_Container *panel = mu_get_current_container(ctx);
+ mu_layout_row(ctx, 1, (int[]) { -1 }, -1);
+ mu_text(ctx, logbuf);
+ mu_end_panel(ctx);
+ if (logbuf_updated) {
+ panel->scroll.y = panel->content_size.y;
+ logbuf_updated = 0;
+ }
+
+ /* input textbox + submit button */
+ static char buf[128];
+ int submitted = 0;
+ mu_layout_row(ctx, 2, (int[]) { -70, -1 }, 0);
+ if (mu_textbox(ctx, buf, sizeof(buf)) & MU_RES_SUBMIT) {
+ mu_set_focus(ctx, ctx->last_id);
+ submitted = 1;
+ }
+ if (mu_button(ctx, "Submit")) { submitted = 1; }
+ if (submitted) {
+ write_log(buf);
+ buf[0] = '\0';
+ }
+
+ mu_end_window(ctx);
+ }
+}
+
+static int uint8_slider(mu_Context *ctx, unsigned char *value, int low, int high) {
+ static float tmp;
+ mu_push_id(ctx, &value, sizeof(value));
+ tmp = *value;
+ int res = mu_slider_ex(ctx, &tmp, low, high, 0, "%.0f", MU_OPT_ALIGNCENTER);
+ *value = tmp;
+ mu_pop_id(ctx);
+ return res;
+}
+
+static void style_window(mu_Context *ctx) {
+ static struct { const char *label; int idx; } colors[] = {
+ { "text:", MU_COLOR_TEXT },
+ { "border:", MU_COLOR_BORDER },
+ { "windowbg:", MU_COLOR_WINDOWBG },
+ { "titlebg:", MU_COLOR_TITLEBG },
+ { "titletext:", MU_COLOR_TITLETEXT },
+ { "panelbg:", MU_COLOR_PANELBG },
+ { "button:", MU_COLOR_BUTTON },
+ { "buttonhover:", MU_COLOR_BUTTONHOVER },
+ { "buttonfocus:", MU_COLOR_BUTTONFOCUS },
+ { "base:", MU_COLOR_BASE },
+ { "basehover:", MU_COLOR_BASEHOVER },
+ { "basefocus:", MU_COLOR_BASEFOCUS },
+ { "scrollbase:", MU_COLOR_SCROLLBASE },
+ { "scrollthumb:", MU_COLOR_SCROLLTHUMB },
+ { NULL }
+ };
+
+ if (mu_begin_window(ctx, "Style Editor", mu_rect(350, 250, 300, 240))) {
+ int sw = mu_get_current_container(ctx)->body.w * 0.14;
+ mu_layout_row(ctx, 6, (int[]) { 80, sw, sw, sw, sw, -1 }, 0);
+ for (int i = 0; colors[i].label; i++) {
+ mu_label(ctx, colors[i].label);
+ uint8_slider(ctx, &ctx->style->colors[i].r, 0, 255);
+ uint8_slider(ctx, &ctx->style->colors[i].g, 0, 255);
+ uint8_slider(ctx, &ctx->style->colors[i].b, 0, 255);
+ uint8_slider(ctx, &ctx->style->colors[i].a, 0, 255);
+ mu_draw_rect(ctx, mu_layout_next(ctx), ctx->style->colors[i]);
+ }
+ mu_end_window(ctx);
+ }
+}
+
+static void process_frame(mu_Context *ctx) {
+ mu_begin(ctx);
+ style_window(ctx);
+ log_window(ctx);
+ test_window(ctx);
+ mu_end(ctx);
+}
+
+
+
+static int text_width(mu_Font font, const char *text, int len) {
+ //if (len == -1) { len = strlen(text); }
+ return MeasureText(text, font_size) * global_scale;
+}
+
+static int text_height(mu_Font font) {
+ return font_size * global_scale;
+}
+
+// Raylib uses MOUSE_LEFT_BUTTON, etc., which are ints from 0-2.
+static const int button_map[3] = {
+ [MOUSE_LEFT_BUTTON] = MU_MOUSE_LEFT,
+ [MOUSE_RIGHT_BUTTON] = MU_MOUSE_RIGHT,
+ [MOUSE_MIDDLE_BUTTON] = MU_MOUSE_MIDDLE
+};
+
+static const char key_map[512] = {
+ [KEY_LEFT_SHIFT] = MU_KEY_SHIFT,
+ [KEY_RIGHT_SHIFT] = MU_KEY_SHIFT,
+ [KEY_LEFT_CONTROL] = MU_KEY_CTRL,
+ [KEY_RIGHT_CONTROL]= MU_KEY_CTRL,
+ [KEY_LEFT_ALT] = MU_KEY_ALT,
+ [KEY_RIGHT_ALT] = MU_KEY_ALT,
+ [KEY_ENTER] = MU_KEY_RETURN,
+ [KEY_BACKSPACE] = MU_KEY_BACKSPACE
+};
+
+void window();
+
+int main(void) {
+ const int screenWidth = 800;
+ const int screenHeight = 400;
+
+
+
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+
+
+
+ mu_Context *ctx = malloc(sizeof(mu_Context));
+ mu_init(ctx);
+
+ ctx->text_height = text_height;
+ ctx->text_width = text_width;
+
+ float sliderValue = 0;
+
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+
+
+ // Inside your frame loop
+ Vector2 mouse = GetMousePosition();
+ mu_input_mousemove(ctx, (int)mouse.x / global_scale, (int)mouse.y / global_scale);
+ // Mouse button handling
+ if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
+ mu_input_mousedown(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_LEFT);
+ }
+ else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
+ // Keep MouseDown state active while button is held
+ ctx->mouse_down |= MU_MOUSE_LEFT;
+ }
+ if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {
+ mu_input_mouseup(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_LEFT);
+ }
+ if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) {
+ mu_input_mousedown(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_RIGHT);
+ }
+ if (IsMouseButtonReleased(MOUSE_RIGHT_BUTTON)) {
+ mu_input_mouseup(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_RIGHT);
+ }
+ if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) {
+ mu_input_mousedown(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_MIDDLE);
+ }
+ if (IsMouseButtonReleased(MOUSE_MIDDLE_BUTTON)) {
+ mu_input_mouseup(ctx, mouse.x / global_scale, mouse.y / global_scale, MU_MOUSE_MIDDLE);
+ }
+
+ // Keyboard key handling
+ for (int key = 0; key < 512; key++) {
+ if (IsKeyPressed(key)) {
+ int c = key_map[key & 0xff];
+ if (c) mu_input_keydown(ctx, c);
+ }
+ if (IsKeyReleased(key)) {
+ int c = key_map[key & 0xff];
+ if (c) mu_input_keyup(ctx, c);
+ }
+ }
+
+ int ch;
+ while ((ch = GetCharPressed()) != 0) {
+ if (ch >= 32 && ch <= 126) { // Printable ASCII range
+ char c = (char)ch;
+ char str[2] = { c, 0 }; // Create null-terminated string
+ mu_input_text(ctx, str);
+ }
+ }
+
+
+ // process_frame(ctx);
+ mu_begin(ctx);
+ window(ctx, &sliderValue);
+ mu_end(ctx);
+
+ BeginDrawing();
+
+ ClearBackground((Color){0,0,0,1});
+
+ mu_Command *cmd = NULL;
+
+ while (mu_next_command(ctx, &cmd)) {
+ switch (cmd->type) {
+ case MU_COMMAND_TEXT: DrawText(cmd->text.str, cmd->text.pos.x * global_scale, cmd->text.pos.y * global_scale, font_size * global_scale, (Color){cmd->text.color.r, cmd->text.color.g, cmd->text.color.b, cmd->text.color.a});
+ break;
+ case MU_COMMAND_RECT: DrawRectangle(cmd->rect.rect.x * global_scale, cmd->rect.rect.y * global_scale,cmd->rect.rect.w * global_scale, cmd->rect.rect.h * global_scale, (Color){cmd->rect.color.r, cmd->rect.color.g, cmd->rect.color.b, cmd->rect.color.a});
+ break;
+ case MU_COMMAND_ICON:
+ //DrawRectangle(cmd->icon.rect.x, cmd->icon.rect.y,cmd->icon.rect.w, cmd->icon.rect.h, (Color){cmd->icon.color.r, cmd->icon.color.g, cmd->icon.color.b, cmd->icon.color.a});
+ break;
+ case MU_COMMAND_CLIP: BeginScissorMode(cmd->rect.rect.x * global_scale, cmd->rect.rect.y * global_scale,cmd->rect.rect.w * global_scale, cmd->rect.rect.h * global_scale); break;
+ }
+ }
+
+ EndDrawing();
+ }
+ CloseWindow();
+ return 0;
+}
+
+void window(mu_Context* ctx, float* value) {
+ if (mu_begin_window_ex(ctx, "Test Window", mu_rect(50, 50, 300, 200), 0)) {
+ // Set up a row with 2 columns: left for sliders/text, right for grid
+ int widths[] = {120, 160};
+ mu_layout_row(ctx, 2, widths, 0);
+
+ // --- Left column ---
+ {
+ mu_layout_row(ctx, 1, (int[]){120}, 0);
+ static float slider1 = 50.0f, slider2 = 0.0f;
+ mu_slider_ex(ctx, &slider1, 0.0f, 100.0f, 1.0f, "%.1f", MU_OPT_ALIGNCENTER);
+ mu_slider_ex(ctx, &slider2, 0.0f, 100.0f, 1.0f, "%.1f", MU_OPT_ALIGNCENTER);
+ mu_text(ctx, "Other controls here...");
+
+ // Test TextboxRaw
+ static char textbox_buf[32] = "Edit me!";
+ mu_Rect textbox_rect = mu_layout_next(ctx);
+ if (mu_textbox_raw(ctx, textbox_buf, sizeof(textbox_buf), 12345, textbox_rect, 0)) {
+ printf("Textbox changed: %s\n", textbox_buf);
+ }
+ }
+
+ // --- Right column ---
+ {
+ mu_layout_row(ctx, 1, (int[]){160}, 0);
+ mu_text(ctx, "4x4 Grid of Buttons with 'x' labels:");
+ for (int row = 0; row < 4; row++) {
+ int grid_widths[4] = {25, 25, 25, 25};
+ mu_layout_row(ctx, 4, grid_widths, 0);
+ for (int col = 0; col < 4; col++) {
+ char id[16];
+ snprintf(id, sizeof(id), "grid_%d_%d", row, col);
+ mu_push_id(ctx, id, strlen(id));
+ if (mu_button_ex(ctx, "x", 0, MU_OPT_ALIGNCENTER)) {
+ // handle click
+ }
+ mu_pop_id(ctx);
+ }
+ }
+ }
+
+ mu_end_window(ctx);
+}
+}
+
diff --git a/MicroUICVersion/microui.c b/MicroUICVersion/microui.c
new file mode 100644
index 0000000..218dad7
--- /dev/null
+++ b/MicroUICVersion/microui.c
@@ -0,0 +1,1209 @@
+/*
+** Copyright (c) 2024 rxi
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to
+** deal in the Software without restriction, including without limitation the
+** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+** sell copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+** IN THE SOFTWARE.
+*/
+
+#include
+#include
+#include
+#include "microui.h"
+
+#define unused(x) ((void) (x))
+
+#define expect(x) do { \
+ if (!(x)) { \
+ fprintf(stderr, "Fatal error: %s:%d: assertion '%s' failed\n", \
+ __FILE__, __LINE__, #x); \
+ abort(); \
+ } \
+ } while (0)
+
+#define push(stk, val) do { \
+ expect((stk).idx < (int) (sizeof((stk).items) / sizeof(*(stk).items))); \
+ (stk).items[(stk).idx] = (val); \
+ (stk).idx++; /* incremented after incase `val` uses this value */ \
+ } while (0)
+
+#define pop(stk) do { \
+ expect((stk).idx > 0); \
+ (stk).idx--; \
+ } while (0)
+
+
+static mu_Rect unclipped_rect = { 0, 0, 0x1000000, 0x1000000 };
+
+static mu_Style default_style = {
+ /* font | size | padding | spacing | indent */
+ NULL, { 68, 10 }, 5, 4, 24,
+ /* title_height | scrollbar_size | thumb_size */
+ 24, 12, 8,
+ {
+ { 230, 230, 230, 255 }, /* MU_COLOR_TEXT */
+ { 25, 25, 25, 255 }, /* MU_COLOR_BORDER */
+ { 50, 50, 50, 255 }, /* MU_COLOR_WINDOWBG */
+ { 25, 25, 25, 255 }, /* MU_COLOR_TITLEBG */
+ { 240, 240, 240, 255 }, /* MU_COLOR_TITLETEXT */
+ { 0, 0, 0, 0 }, /* MU_COLOR_PANELBG */
+ { 75, 75, 75, 255 }, /* MU_COLOR_BUTTON */
+ { 95, 95, 95, 255 }, /* MU_COLOR_BUTTONHOVER */
+ { 115, 115, 115, 255 }, /* MU_COLOR_BUTTONFOCUS */
+ { 30, 30, 30, 255 }, /* MU_COLOR_BASE */
+ { 35, 35, 35, 255 }, /* MU_COLOR_BASEHOVER */
+ { 40, 40, 40, 255 }, /* MU_COLOR_BASEFOCUS */
+ { 43, 43, 43, 255 }, /* MU_COLOR_SCROLLBASE */
+ { 30, 30, 30, 255 } /* MU_COLOR_SCROLLTHUMB */
+ }
+};
+
+
+mu_Vec2 mu_vec2(int x, int y) {
+ mu_Vec2 res;
+ res.x = x; res.y = y;
+ return res;
+}
+
+
+mu_Rect mu_rect(int x, int y, int w, int h) {
+ mu_Rect res;
+ res.x = x; res.y = y; res.w = w; res.h = h;
+ return res;
+}
+
+
+mu_Color mu_color(int r, int g, int b, int a) {
+ mu_Color res;
+ res.r = r; res.g = g; res.b = b; res.a = a;
+ return res;
+}
+
+
+static mu_Rect expand_rect(mu_Rect rect, int n) {
+ return mu_rect(rect.x - n, rect.y - n, rect.w + n * 2, rect.h + n * 2);
+}
+
+
+static mu_Rect intersect_rects(mu_Rect r1, mu_Rect r2) {
+ int x1 = mu_max(r1.x, r2.x);
+ int y1 = mu_max(r1.y, r2.y);
+ int x2 = mu_min(r1.x + r1.w, r2.x + r2.w);
+ int y2 = mu_min(r1.y + r1.h, r2.y + r2.h);
+ if (x2 < x1) { x2 = x1; }
+ if (y2 < y1) { y2 = y1; }
+ return mu_rect(x1, y1, x2 - x1, y2 - y1);
+}
+
+
+static int rect_overlaps_vec2(mu_Rect r, mu_Vec2 p) {
+ return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h;
+}
+
+
+static void draw_frame(mu_Context *ctx, mu_Rect rect, int colorid) {
+ mu_draw_rect(ctx, rect, ctx->style->colors[colorid]);
+ if (colorid == MU_COLOR_SCROLLBASE ||
+ colorid == MU_COLOR_SCROLLTHUMB ||
+ colorid == MU_COLOR_TITLEBG) { return; }
+ /* draw border */
+ if (ctx->style->colors[MU_COLOR_BORDER].a) {
+ mu_draw_box(ctx, expand_rect(rect, 1), ctx->style->colors[MU_COLOR_BORDER]);
+ }
+}
+
+
+void mu_init(mu_Context *ctx) {
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->draw_frame = draw_frame;
+ ctx->_style = default_style;
+ ctx->style = &ctx->_style;
+}
+
+
+void mu_begin(mu_Context *ctx) {
+ expect(ctx->text_width && ctx->text_height);
+ ctx->command_list.idx = 0;
+ ctx->root_list.idx = 0;
+ ctx->scroll_target = NULL;
+ ctx->hover_root = ctx->next_hover_root;
+ ctx->next_hover_root = NULL;
+ ctx->mouse_delta.x = ctx->mouse_pos.x - ctx->last_mouse_pos.x;
+ ctx->mouse_delta.y = ctx->mouse_pos.y - ctx->last_mouse_pos.y;
+ ctx->frame++;
+}
+
+
+static int compare_zindex(const void *a, const void *b) {
+ return (*(mu_Container**) a)->zindex - (*(mu_Container**) b)->zindex;
+}
+
+
+void mu_end(mu_Context *ctx) {
+ int i, n;
+ /* check stacks */
+ expect(ctx->container_stack.idx == 0);
+ expect(ctx->clip_stack.idx == 0);
+ expect(ctx->id_stack.idx == 0);
+ expect(ctx->layout_stack.idx == 0);
+
+ /* handle scroll input */
+ if (ctx->scroll_target) {
+ ctx->scroll_target->scroll.x += ctx->scroll_delta.x;
+ ctx->scroll_target->scroll.y += ctx->scroll_delta.y;
+ }
+
+ /* unset focus if focus id was not touched this frame */
+ if (!ctx->updated_focus) { ctx->focus = 0; }
+ ctx->updated_focus = 0;
+
+ /* bring hover root to front if mouse was pressed */
+ if (ctx->mouse_pressed && ctx->next_hover_root &&
+ ctx->next_hover_root->zindex < ctx->last_zindex &&
+ ctx->next_hover_root->zindex >= 0
+ ) {
+ mu_bring_to_front(ctx, ctx->next_hover_root);
+ }
+
+ /* reset input state */
+ ctx->key_pressed = 0;
+ ctx->input_text[0] = '\0';
+ ctx->mouse_pressed = 0;
+ ctx->scroll_delta = mu_vec2(0, 0);
+ ctx->last_mouse_pos = ctx->mouse_pos;
+
+ /* sort root containers by zindex */
+ n = ctx->root_list.idx;
+ qsort(ctx->root_list.items, n, sizeof(mu_Container*), compare_zindex);
+
+ /* set root container jump commands */
+ for (i = 0; i < n; i++) {
+ mu_Container *cnt = ctx->root_list.items[i];
+ /* if this is the first container then make the first command jump to it.
+ ** otherwise set the previous container's tail to jump to this one */
+ if (i == 0) {
+ mu_Command *cmd = (mu_Command*) ctx->command_list.items;
+ cmd->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand);
+ } else {
+ mu_Container *prev = ctx->root_list.items[i - 1];
+ prev->tail->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand);
+ }
+ /* make the last container's tail jump to the end of command list */
+ if (i == n - 1) {
+ cnt->tail->jump.dst = ctx->command_list.items + ctx->command_list.idx;
+ }
+ }
+}
+
+
+void mu_set_focus(mu_Context *ctx, mu_Id id) {
+ ctx->focus = id;
+ ctx->updated_focus = 1;
+}
+
+
+/* 32bit fnv-1a hash */
+#define HASH_INITIAL 2166136261
+
+static void hash(mu_Id *hash, const void *data, int size) {
+ const unsigned char *p = data;
+ while (size--) {
+ *hash = (*hash ^ *p++) * 16777619;
+ }
+}
+
+
+mu_Id mu_get_id(mu_Context *ctx, const void *data, int size) {
+ int idx = ctx->id_stack.idx;
+ mu_Id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : HASH_INITIAL;
+ hash(&res, data, size);
+ ctx->last_id = res;
+ return res;
+}
+
+
+void mu_push_id(mu_Context *ctx, const void *data, int size) {
+ push(ctx->id_stack, mu_get_id(ctx, data, size));
+}
+
+
+void mu_pop_id(mu_Context *ctx) {
+ pop(ctx->id_stack);
+}
+
+
+void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect) {
+ mu_Rect last = mu_get_clip_rect(ctx);
+ push(ctx->clip_stack, intersect_rects(rect, last));
+}
+
+
+void mu_pop_clip_rect(mu_Context *ctx) {
+ pop(ctx->clip_stack);
+}
+
+
+mu_Rect mu_get_clip_rect(mu_Context *ctx) {
+ expect(ctx->clip_stack.idx > 0);
+ return ctx->clip_stack.items[ctx->clip_stack.idx - 1];
+}
+
+
+int mu_check_clip(mu_Context *ctx, mu_Rect r) {
+ mu_Rect cr = mu_get_clip_rect(ctx);
+ if (r.x > cr.x + cr.w || r.x + r.w < cr.x ||
+ r.y > cr.y + cr.h || r.y + r.h < cr.y ) { return MU_CLIP_ALL; }
+ if (r.x >= cr.x && r.x + r.w <= cr.x + cr.w &&
+ r.y >= cr.y && r.y + r.h <= cr.y + cr.h ) { return 0; }
+ return MU_CLIP_PART;
+}
+
+
+static void push_layout(mu_Context *ctx, mu_Rect body, mu_Vec2 scroll) {
+ mu_Layout layout;
+ int width = 0;
+ memset(&layout, 0, sizeof(layout));
+ layout.body = mu_rect(body.x - scroll.x, body.y - scroll.y, body.w, body.h);
+ layout.max = mu_vec2(-0x1000000, -0x1000000);
+ push(ctx->layout_stack, layout);
+ mu_layout_row(ctx, 1, &width, 0);
+}
+
+
+static mu_Layout* get_layout(mu_Context *ctx) {
+ return &ctx->layout_stack.items[ctx->layout_stack.idx - 1];
+}
+
+
+static void pop_container(mu_Context *ctx) {
+ mu_Container *cnt = mu_get_current_container(ctx);
+ mu_Layout *layout = get_layout(ctx);
+ cnt->content_size.x = layout->max.x - layout->body.x;
+ cnt->content_size.y = layout->max.y - layout->body.y;
+ /* pop container, layout and id */
+ pop(ctx->container_stack);
+ pop(ctx->layout_stack);
+ mu_pop_id(ctx);
+}
+
+
+mu_Container* mu_get_current_container(mu_Context *ctx) {
+ expect(ctx->container_stack.idx > 0);
+ return ctx->container_stack.items[ ctx->container_stack.idx - 1 ];
+}
+
+
+static mu_Container* get_container(mu_Context *ctx, mu_Id id, int opt) {
+ mu_Container *cnt;
+ /* try to get existing container from pool */
+ int idx = mu_pool_get(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id);
+ if (idx >= 0) {
+ if (ctx->containers[idx].open || ~opt & MU_OPT_CLOSED) {
+ mu_pool_update(ctx, ctx->container_pool, idx);
+ }
+ return &ctx->containers[idx];
+ }
+ if (opt & MU_OPT_CLOSED) { return NULL; }
+ /* container not found in pool: init new container */
+ idx = mu_pool_init(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id);
+ cnt = &ctx->containers[idx];
+ memset(cnt, 0, sizeof(*cnt));
+ cnt->open = 1;
+ mu_bring_to_front(ctx, cnt);
+ return cnt;
+}
+
+
+mu_Container* mu_get_container(mu_Context *ctx, const char *name) {
+ mu_Id id = mu_get_id(ctx, name, strlen(name));
+ return get_container(ctx, id, 0);
+}
+
+
+void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt) {
+ cnt->zindex = ++ctx->last_zindex;
+}
+
+
+/*============================================================================
+** pool
+**============================================================================*/
+
+int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) {
+ int i, n = -1, f = ctx->frame;
+ for (i = 0; i < len; i++) {
+ if (items[i].last_update < f) {
+ f = items[i].last_update;
+ n = i;
+ }
+ }
+ expect(n > -1);
+ items[n].id = id;
+ mu_pool_update(ctx, items, n);
+ return n;
+}
+
+
+int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) {
+ int i;
+ unused(ctx);
+ for (i = 0; i < len; i++) {
+ if (items[i].id == id) { return i; }
+ }
+ return -1;
+}
+
+
+void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx) {
+ items[idx].last_update = ctx->frame;
+}
+
+
+/*============================================================================
+** input handlers
+**============================================================================*/
+
+void mu_input_mousemove(mu_Context *ctx, int x, int y) {
+ ctx->mouse_pos = mu_vec2(x, y);
+}
+
+
+void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn) {
+ mu_input_mousemove(ctx, x, y);
+ ctx->mouse_down |= btn;
+ ctx->mouse_pressed |= btn;
+}
+
+
+void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn) {
+ mu_input_mousemove(ctx, x, y);
+ ctx->mouse_down &= ~btn;
+}
+
+
+void mu_input_scroll(mu_Context *ctx, int x, int y) {
+ ctx->scroll_delta.x += x;
+ ctx->scroll_delta.y += y;
+}
+
+
+void mu_input_keydown(mu_Context *ctx, int key) {
+ ctx->key_pressed |= key;
+ ctx->key_down |= key;
+}
+
+
+void mu_input_keyup(mu_Context *ctx, int key) {
+ ctx->key_down &= ~key;
+}
+
+
+void mu_input_text(mu_Context *ctx, const char *text) {
+ int len = strlen(ctx->input_text);
+ int size = strlen(text) + 1;
+ expect(len + size <= (int) sizeof(ctx->input_text));
+ memcpy(ctx->input_text + len, text, size);
+}
+
+
+/*============================================================================
+** commandlist
+**============================================================================*/
+
+mu_Command* mu_push_command(mu_Context *ctx, int type, int size) {
+ mu_Command *cmd = (mu_Command*) (ctx->command_list.items + ctx->command_list.idx);
+ expect(ctx->command_list.idx + size < MU_COMMANDLIST_SIZE);
+ cmd->base.type = type;
+ cmd->base.size = size;
+ ctx->command_list.idx += size;
+ return cmd;
+}
+
+
+int mu_next_command(mu_Context *ctx, mu_Command **cmd) {
+ if (*cmd) {
+ *cmd = (mu_Command*) (((char*) *cmd) + (*cmd)->base.size);
+ } else {
+ *cmd = (mu_Command*) ctx->command_list.items;
+ }
+ while ((char*) *cmd != ctx->command_list.items + ctx->command_list.idx) {
+ if ((*cmd)->type != MU_COMMAND_JUMP) { return 1; }
+ *cmd = (*cmd)->jump.dst;
+ }
+ return 0;
+}
+
+
+static mu_Command* push_jump(mu_Context *ctx, mu_Command *dst) {
+ mu_Command *cmd;
+ cmd = mu_push_command(ctx, MU_COMMAND_JUMP, sizeof(mu_JumpCommand));
+ cmd->jump.dst = dst;
+ return cmd;
+}
+
+
+void mu_set_clip(mu_Context *ctx, mu_Rect rect) {
+ mu_Command *cmd;
+ cmd = mu_push_command(ctx, MU_COMMAND_CLIP, sizeof(mu_ClipCommand));
+ cmd->clip.rect = rect;
+}
+
+
+void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color) {
+ mu_Command *cmd;
+ rect = intersect_rects(rect, mu_get_clip_rect(ctx));
+ if (rect.w > 0 && rect.h > 0) {
+ cmd = mu_push_command(ctx, MU_COMMAND_RECT, sizeof(mu_RectCommand));
+ cmd->rect.rect = rect;
+ cmd->rect.color = color;
+ }
+}
+
+
+void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color) {
+ mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y, rect.w - 2, 1), color);
+ mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y + rect.h - 1, rect.w - 2, 1), color);
+ mu_draw_rect(ctx, mu_rect(rect.x, rect.y, 1, rect.h), color);
+ mu_draw_rect(ctx, mu_rect(rect.x + rect.w - 1, rect.y, 1, rect.h), color);
+}
+
+
+void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len,
+ mu_Vec2 pos, mu_Color color)
+{
+ mu_Command *cmd;
+ mu_Rect rect = mu_rect(
+ pos.x, pos.y, ctx->text_width(font, str, len), ctx->text_height(font));
+ int clipped = mu_check_clip(ctx, rect);
+ if (clipped == MU_CLIP_ALL ) { return; }
+ if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); }
+ /* add command */
+ if (len < 0) { len = strlen(str); }
+ cmd = mu_push_command(ctx, MU_COMMAND_TEXT, sizeof(mu_TextCommand) + len);
+ memcpy(cmd->text.str, str, len);
+ cmd->text.str[len] = '\0';
+ cmd->text.pos = pos;
+ cmd->text.color = color;
+ cmd->text.font = font;
+ /* reset clipping if it was set */
+ if (clipped) { mu_set_clip(ctx, unclipped_rect); }
+}
+
+
+void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color) {
+ mu_Command *cmd;
+ /* do clip command if the rect isn't fully contained within the cliprect */
+ int clipped = mu_check_clip(ctx, rect);
+ if (clipped == MU_CLIP_ALL ) { return; }
+ if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); }
+ /* do icon command */
+ cmd = mu_push_command(ctx, MU_COMMAND_ICON, sizeof(mu_IconCommand));
+ cmd->icon.id = id;
+ cmd->icon.rect = rect;
+ cmd->icon.color = color;
+ /* reset clipping if it was set */
+ if (clipped) { mu_set_clip(ctx, unclipped_rect); }
+}
+
+
+/*============================================================================
+** layout
+**============================================================================*/
+
+enum { RELATIVE = 1, ABSOLUTE = 2 };
+
+
+void mu_layout_begin_column(mu_Context *ctx) {
+ push_layout(ctx, mu_layout_next(ctx), mu_vec2(0, 0));
+}
+
+
+void mu_layout_end_column(mu_Context *ctx) {
+ mu_Layout *a, *b;
+ b = get_layout(ctx);
+ pop(ctx->layout_stack);
+ /* inherit position/next_row/max from child layout if they are greater */
+ a = get_layout(ctx);
+ a->position.x = mu_max(a->position.x, b->position.x + b->body.x - a->body.x);
+ a->next_row = mu_max(a->next_row, b->next_row + b->body.y - a->body.y);
+ a->max.x = mu_max(a->max.x, b->max.x);
+ a->max.y = mu_max(a->max.y, b->max.y);
+}
+
+
+void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height) {
+ mu_Layout *layout = get_layout(ctx);
+ if (widths) {
+ expect(items <= MU_MAX_WIDTHS);
+ memcpy(layout->widths, widths, items * sizeof(widths[0]));
+ }
+ layout->items = items;
+ layout->position = mu_vec2(layout->indent, layout->next_row);
+ layout->size.y = height;
+ layout->item_index = 0;
+}
+
+
+void mu_layout_width(mu_Context *ctx, int width) {
+ get_layout(ctx)->size.x = width;
+}
+
+
+void mu_layout_height(mu_Context *ctx, int height) {
+ get_layout(ctx)->size.y = height;
+}
+
+
+void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative) {
+ mu_Layout *layout = get_layout(ctx);
+ layout->next = r;
+ layout->next_type = relative ? RELATIVE : ABSOLUTE;
+}
+
+
+mu_Rect mu_layout_next(mu_Context *ctx) {
+ mu_Layout *layout = get_layout(ctx);
+ mu_Style *style = ctx->style;
+ mu_Rect res;
+
+ if (layout->next_type) {
+ /* handle rect set by `mu_layout_set_next` */
+ int type = layout->next_type;
+ layout->next_type = 0;
+ res = layout->next;
+ if (type == ABSOLUTE) { return (ctx->last_rect = res); }
+
+ } else {
+ /* handle next row */
+ if (layout->item_index == layout->items) {
+ mu_layout_row(ctx, layout->items, NULL, layout->size.y);
+ }
+
+ /* position */
+ res.x = layout->position.x;
+ res.y = layout->position.y;
+
+ /* size */
+ res.w = layout->items > 0 ? layout->widths[layout->item_index] : layout->size.x;
+ res.h = layout->size.y;
+ if (res.w == 0) { res.w = style->size.x + style->padding * 2; }
+ if (res.h == 0) { res.h = style->size.y + style->padding * 2; }
+ if (res.w < 0) { res.w += layout->body.w - res.x + 1; }
+ if (res.h < 0) { res.h += layout->body.h - res.y + 1; }
+
+ layout->item_index++;
+ }
+
+ /* update position */
+ layout->position.x += res.w + style->spacing;
+ layout->next_row = mu_max(layout->next_row, res.y + res.h + style->spacing);
+
+ /* apply body offset */
+ res.x += layout->body.x;
+ res.y += layout->body.y;
+
+ /* update max position */
+ layout->max.x = mu_max(layout->max.x, res.x + res.w);
+ layout->max.y = mu_max(layout->max.y, res.y + res.h);
+
+ return (ctx->last_rect = res);
+}
+
+
+/*============================================================================
+** controls
+**============================================================================*/
+
+static int in_hover_root(mu_Context *ctx) {
+ int i = ctx->container_stack.idx;
+ while (i--) {
+ if (ctx->container_stack.items[i] == ctx->hover_root) { return 1; }
+ /* only root containers have their `head` field set; stop searching if we've
+ ** reached the current root container */
+ if (ctx->container_stack.items[i]->head) { break; }
+ }
+ return 0;
+}
+
+
+void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect,
+ int colorid, int opt)
+{
+ if (opt & MU_OPT_NOFRAME) { return; }
+ colorid += (ctx->focus == id) ? 2 : (ctx->hover == id) ? 1 : 0;
+ ctx->draw_frame(ctx, rect, colorid);
+}
+
+
+void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect,
+ int colorid, int opt)
+{
+ mu_Vec2 pos;
+ mu_Font font = ctx->style->font;
+ int tw = ctx->text_width(font, str, -1);
+ mu_push_clip_rect(ctx, rect);
+ pos.y = rect.y + (rect.h - ctx->text_height(font)) / 2;
+ if (opt & MU_OPT_ALIGNCENTER) {
+ pos.x = rect.x + (rect.w - tw) / 2;
+ } else if (opt & MU_OPT_ALIGNRIGHT) {
+ pos.x = rect.x + rect.w - tw - ctx->style->padding;
+ } else {
+ pos.x = rect.x + ctx->style->padding;
+ }
+ mu_draw_text(ctx, font, str, -1, pos, ctx->style->colors[colorid]);
+ mu_pop_clip_rect(ctx);
+}
+
+
+int mu_mouse_over(mu_Context *ctx, mu_Rect rect) {
+ return rect_overlaps_vec2(rect, ctx->mouse_pos) &&
+ rect_overlaps_vec2(mu_get_clip_rect(ctx), ctx->mouse_pos) &&
+ in_hover_root(ctx);
+}
+
+
+void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt) {
+ int mouseover = mu_mouse_over(ctx, rect);
+
+ if (ctx->focus == id) { ctx->updated_focus = 1; }
+ if (opt & MU_OPT_NOINTERACT) { return; }
+ if (mouseover && !ctx->mouse_down) { ctx->hover = id; }
+
+ if (ctx->focus == id) {
+ if (ctx->mouse_pressed && !mouseover) { mu_set_focus(ctx, 0); }
+ if (!ctx->mouse_down && ~opt & MU_OPT_HOLDFOCUS) { mu_set_focus(ctx, 0); }
+ }
+
+ if (ctx->hover == id) {
+ if (ctx->mouse_pressed) {
+ mu_set_focus(ctx, id);
+ } else if (!mouseover) {
+ ctx->hover = 0;
+ }
+ }
+}
+
+
+void mu_text(mu_Context *ctx, const char *text) {
+ const char *start, *end, *p = text;
+ int width = -1;
+ mu_Font font = ctx->style->font;
+ mu_Color color = ctx->style->colors[MU_COLOR_TEXT];
+ mu_layout_begin_column(ctx);
+ mu_layout_row(ctx, 1, &width, ctx->text_height(font));
+ do {
+ mu_Rect r = mu_layout_next(ctx);
+ int w = 0;
+ start = end = p;
+ do {
+ const char* word = p;
+ while (*p && *p != ' ' && *p != '\n') { p++; }
+ w += ctx->text_width(font, word, p - word);
+ if (w > r.w && end != start) { break; }
+ w += ctx->text_width(font, p, 1);
+ end = p++;
+ } while (*end && *end != '\n');
+ mu_draw_text(ctx, font, start, end - start, mu_vec2(r.x, r.y), color);
+ p = end + 1;
+ } while (*end);
+ mu_layout_end_column(ctx);
+}
+
+
+void mu_label(mu_Context *ctx, const char *text) {
+ mu_draw_control_text(ctx, text, mu_layout_next(ctx), MU_COLOR_TEXT, 0);
+}
+
+
+int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt) {
+ int res = 0;
+ mu_Id id = label ? mu_get_id(ctx, label, strlen(label))
+ : mu_get_id(ctx, &icon, sizeof(icon));
+ mu_Rect r = mu_layout_next(ctx);
+ mu_update_control(ctx, id, r, opt);
+ /* handle click */
+ if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) {
+ res |= MU_RES_SUBMIT;
+ }
+ /* draw */
+ mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, opt);
+ if (label) { mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, opt); }
+ if (icon) { mu_draw_icon(ctx, icon, r, ctx->style->colors[MU_COLOR_TEXT]); }
+ return res;
+}
+
+
+int mu_checkbox(mu_Context *ctx, const char *label, int *state) {
+ int res = 0;
+ mu_Id id = mu_get_id(ctx, &state, sizeof(state));
+ mu_Rect r = mu_layout_next(ctx);
+ mu_Rect box = mu_rect(r.x, r.y, r.h, r.h);
+ mu_update_control(ctx, id, r, 0);
+ /* handle click */
+ if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) {
+ res |= MU_RES_CHANGE;
+ *state = !*state;
+ }
+ /* draw */
+ mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0);
+ if (*state) {
+ mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]);
+ }
+ r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h);
+ mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
+ return res;
+}
+
+
+int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r,
+ int opt)
+{
+ int res = 0;
+ mu_update_control(ctx, id, r, opt | MU_OPT_HOLDFOCUS);
+
+ if (ctx->focus == id) {
+ /* handle text input */
+ int len = strlen(buf);
+ int n = mu_min(bufsz - len - 1, (int) strlen(ctx->input_text));
+ if (n > 0) {
+ memcpy(buf + len, ctx->input_text, n);
+ len += n;
+ buf[len] = '\0';
+ res |= MU_RES_CHANGE;
+ }
+ /* handle backspace */
+ if (ctx->key_pressed & MU_KEY_BACKSPACE && len > 0) {
+ /* skip utf-8 continuation bytes */
+ while ((buf[--len] & 0xc0) == 0x80 && len > 0);
+ buf[len] = '\0';
+ res |= MU_RES_CHANGE;
+ }
+ /* handle return */
+ if (ctx->key_pressed & MU_KEY_RETURN) {
+ mu_set_focus(ctx, 0);
+ res |= MU_RES_SUBMIT;
+ }
+ }
+
+ /* draw */
+ mu_draw_control_frame(ctx, id, r, MU_COLOR_BASE, opt);
+ if (ctx->focus == id) {
+ mu_Color color = ctx->style->colors[MU_COLOR_TEXT];
+ mu_Font font = ctx->style->font;
+ int textw = ctx->text_width(font, buf, -1);
+ int texth = ctx->text_height(font);
+ int ofx = r.w - ctx->style->padding - textw - 1;
+ int textx = r.x + mu_min(ofx, ctx->style->padding);
+ int texty = r.y + (r.h - texth) / 2;
+ mu_push_clip_rect(ctx, r);
+ mu_draw_text(ctx, font, buf, -1, mu_vec2(textx, texty), color);
+ mu_draw_rect(ctx, mu_rect(textx + textw, texty, 1, texth), color);
+ mu_pop_clip_rect(ctx);
+ } else {
+ mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, opt);
+ }
+
+ return res;
+}
+
+
+static int number_textbox(mu_Context *ctx, mu_Real *value, mu_Rect r, mu_Id id) {
+ if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->key_down & MU_KEY_SHIFT &&
+ ctx->hover == id
+ ) {
+ ctx->number_edit = id;
+ sprintf(ctx->number_edit_buf, MU_REAL_FMT, *value);
+ }
+ if (ctx->number_edit == id) {
+ int res = mu_textbox_raw(
+ ctx, ctx->number_edit_buf, sizeof(ctx->number_edit_buf), id, r, 0);
+ if (res & MU_RES_SUBMIT || ctx->focus != id) {
+ *value = strtod(ctx->number_edit_buf, NULL);
+ ctx->number_edit = 0;
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt) {
+ mu_Id id = mu_get_id(ctx, &buf, sizeof(buf));
+ mu_Rect r = mu_layout_next(ctx);
+ return mu_textbox_raw(ctx, buf, bufsz, id, r, opt);
+}
+
+
+int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high,
+ mu_Real step, const char *fmt, int opt)
+{
+ char buf[MU_MAX_FMT + 1];
+ mu_Rect thumb;
+ int x, w, res = 0;
+ mu_Real last = *value, v = last;
+ mu_Id id = mu_get_id(ctx, &value, sizeof(value));
+ mu_Rect base = mu_layout_next(ctx);
+
+ /* handle text input mode */
+ if (number_textbox(ctx, &v, base, id)) { return res; }
+
+ /* handle normal mode */
+ mu_update_control(ctx, id, base, opt);
+
+ /* handle input */
+ if (ctx->focus == id &&
+ (ctx->mouse_down | ctx->mouse_pressed) == MU_MOUSE_LEFT)
+ {
+ printf("C Slider dragging - Focus: %d, MouseDown: %d, MousePressed: %d\n", ctx->focus, ctx->mouse_down, ctx->mouse_pressed);
+ v = low + (ctx->mouse_pos.x - base.x) * (high - low) / base.w;
+ if (step) { v = ((long long)((v + step / 2) / step)) * step; }
+ }
+ /* clamp and store value, update res */
+ *value = v = mu_clamp(v, low, high);
+ if (last != v) { res |= MU_RES_CHANGE; }
+
+ /* draw base */
+ mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt);
+ /* draw thumb */
+ w = ctx->style->thumb_size;
+ x = (v - low) * (base.w - w) / (high - low);
+ thumb = mu_rect(base.x + x, base.y, w, base.h);
+ mu_draw_control_frame(ctx, id, thumb, MU_COLOR_BUTTON, opt);
+ /* draw text */
+ sprintf(buf, fmt, v);
+ mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt);
+
+ return res;
+}
+
+
+int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step,
+ const char *fmt, int opt)
+{
+ char buf[MU_MAX_FMT + 1];
+ int res = 0;
+ mu_Id id = mu_get_id(ctx, &value, sizeof(value));
+ mu_Rect base = mu_layout_next(ctx);
+ mu_Real last = *value;
+
+ /* handle text input mode */
+ if (number_textbox(ctx, value, base, id)) { return res; }
+
+ /* handle normal mode */
+ mu_update_control(ctx, id, base, opt);
+
+ /* handle input */
+ if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) {
+ *value += ctx->mouse_delta.x * step;
+ }
+ /* set flag if value changed */
+ if (*value != last) { res |= MU_RES_CHANGE; }
+
+ /* draw base */
+ mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt);
+ /* draw text */
+ sprintf(buf, fmt, *value);
+ mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt);
+
+ return res;
+}
+
+
+static int header(mu_Context *ctx, const char *label, int istreenode, int opt) {
+ mu_Rect r;
+ int active, expanded;
+ mu_Id id = mu_get_id(ctx, label, strlen(label));
+ int idx = mu_pool_get(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id);
+ int width = -1;
+ mu_layout_row(ctx, 1, &width, 0);
+
+ active = (idx >= 0);
+ expanded = (opt & MU_OPT_EXPANDED) ? !active : active;
+ r = mu_layout_next(ctx);
+ mu_update_control(ctx, id, r, 0);
+
+ /* handle click */
+ active ^= (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id);
+
+ /* update pool ref */
+ if (idx >= 0) {
+ if (active) { mu_pool_update(ctx, ctx->treenode_pool, idx); }
+ else { memset(&ctx->treenode_pool[idx], 0, sizeof(mu_PoolItem)); }
+ } else if (active) {
+ mu_pool_init(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id);
+ }
+
+ /* draw */
+ if (istreenode) {
+ if (ctx->hover == id) { ctx->draw_frame(ctx, r, MU_COLOR_BUTTONHOVER); }
+ } else {
+ mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, 0);
+ }
+ mu_draw_icon(
+ ctx, expanded ? MU_ICON_EXPANDED : MU_ICON_COLLAPSED,
+ mu_rect(r.x, r.y, r.h, r.h), ctx->style->colors[MU_COLOR_TEXT]);
+ r.x += r.h - ctx->style->padding;
+ r.w -= r.h - ctx->style->padding;
+ mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
+
+ return expanded ? MU_RES_ACTIVE : 0;
+}
+
+
+int mu_header_ex(mu_Context *ctx, const char *label, int opt) {
+ return header(ctx, label, 0, opt);
+}
+
+
+int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt) {
+ int res = header(ctx, label, 1, opt);
+ if (res & MU_RES_ACTIVE) {
+ get_layout(ctx)->indent += ctx->style->indent;
+ push(ctx->id_stack, ctx->last_id);
+ }
+ return res;
+}
+
+
+void mu_end_treenode(mu_Context *ctx) {
+ get_layout(ctx)->indent -= ctx->style->indent;
+ mu_pop_id(ctx);
+}
+
+
+#define scrollbar(ctx, cnt, b, cs, x, y, w, h) \
+ do { \
+ /* only add scrollbar if content size is larger than body */ \
+ int maxscroll = cs.y - b->h; \
+ \
+ if (maxscroll > 0 && b->h > 0) { \
+ mu_Rect base, thumb; \
+ mu_Id id = mu_get_id(ctx, "!scrollbar" #y, 11); \
+ \
+ /* get sizing / positioning */ \
+ base = *b; \
+ base.x = b->x + b->w; \
+ base.w = ctx->style->scrollbar_size; \
+ \
+ /* handle input */ \
+ mu_update_control(ctx, id, base, 0); \
+ if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) { \
+ cnt->scroll.y += ctx->mouse_delta.y * cs.y / base.h; \
+ } \
+ /* clamp scroll to limits */ \
+ cnt->scroll.y = mu_clamp(cnt->scroll.y, 0, maxscroll); \
+ \
+ /* draw base and thumb */ \
+ ctx->draw_frame(ctx, base, MU_COLOR_SCROLLBASE); \
+ thumb = base; \
+ thumb.h = mu_max(ctx->style->thumb_size, base.h * b->h / cs.y); \
+ thumb.y += cnt->scroll.y * (base.h - thumb.h) / maxscroll; \
+ ctx->draw_frame(ctx, thumb, MU_COLOR_SCROLLTHUMB); \
+ \
+ /* set this as the scroll_target (will get scrolled on mousewheel) */ \
+ /* if the mouse is over it */ \
+ if (mu_mouse_over(ctx, *b)) { ctx->scroll_target = cnt; } \
+ } else { \
+ cnt->scroll.y = 0; \
+ } \
+ } while (0)
+
+
+static void scrollbars(mu_Context *ctx, mu_Container *cnt, mu_Rect *body) {
+ int sz = ctx->style->scrollbar_size;
+ mu_Vec2 cs = cnt->content_size;
+ cs.x += ctx->style->padding * 2;
+ cs.y += ctx->style->padding * 2;
+ mu_push_clip_rect(ctx, *body);
+ /* resize body to make room for scrollbars */
+ if (cs.y > cnt->body.h) { body->w -= sz; }
+ if (cs.x > cnt->body.w) { body->h -= sz; }
+ /* to create a horizontal or vertical scrollbar almost-identical code is
+ ** used; only the references to `x|y` `w|h` need to be switched */
+ scrollbar(ctx, cnt, body, cs, x, y, w, h);
+ scrollbar(ctx, cnt, body, cs, y, x, h, w);
+ mu_pop_clip_rect(ctx);
+}
+
+
+static void push_container_body(
+ mu_Context *ctx, mu_Container *cnt, mu_Rect body, int opt
+) {
+ if (~opt & MU_OPT_NOSCROLL) { scrollbars(ctx, cnt, &body); }
+ push_layout(ctx, expand_rect(body, -ctx->style->padding), cnt->scroll);
+ cnt->body = body;
+}
+
+
+static void begin_root_container(mu_Context *ctx, mu_Container *cnt) {
+ push(ctx->container_stack, cnt);
+ /* push container to roots list and push head command */
+ push(ctx->root_list, cnt);
+ cnt->head = push_jump(ctx, NULL);
+ /* set as hover root if the mouse is overlapping this container and it has a
+ ** higher zindex than the current hover root */
+ if (rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) &&
+ (!ctx->next_hover_root || cnt->zindex > ctx->next_hover_root->zindex)
+ ) {
+ ctx->next_hover_root = cnt;
+ }
+ /* clipping is reset here in case a root-container is made within
+ ** another root-containers's begin/end block; this prevents the inner
+ ** root-container being clipped to the outer */
+ push(ctx->clip_stack, unclipped_rect);
+}
+
+
+static void end_root_container(mu_Context *ctx) {
+ /* push tail 'goto' jump command and set head 'skip' command. the final steps
+ ** on initing these are done in mu_end() */
+ mu_Container *cnt = mu_get_current_container(ctx);
+ cnt->tail = push_jump(ctx, NULL);
+ cnt->head->jump.dst = ctx->command_list.items + ctx->command_list.idx;
+ /* pop base clip rect and container */
+ mu_pop_clip_rect(ctx);
+ pop_container(ctx);
+}
+
+
+int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt) {
+ mu_Rect body;
+ mu_Id id = mu_get_id(ctx, title, strlen(title));
+ mu_Container *cnt = get_container(ctx, id, opt);
+ if (!cnt || !cnt->open) { return 0; }
+ push(ctx->id_stack, id);
+
+ if (cnt->rect.w == 0) { cnt->rect = rect; }
+ begin_root_container(ctx, cnt);
+ rect = body = cnt->rect;
+
+ /* draw frame */
+ if (~opt & MU_OPT_NOFRAME) {
+ ctx->draw_frame(ctx, rect, MU_COLOR_WINDOWBG);
+ }
+
+ /* do title bar */
+ if (~opt & MU_OPT_NOTITLE) {
+ mu_Rect tr = rect;
+ tr.h = ctx->style->title_height;
+ ctx->draw_frame(ctx, tr, MU_COLOR_TITLEBG);
+
+ /* do title text */
+ if (~opt & MU_OPT_NOTITLE) {
+ mu_Id id = mu_get_id(ctx, "!title", 6);
+ mu_update_control(ctx, id, tr, opt);
+ mu_draw_control_text(ctx, title, tr, MU_COLOR_TITLETEXT, opt);
+ if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) {
+ cnt->rect.x += ctx->mouse_delta.x;
+ cnt->rect.y += ctx->mouse_delta.y;
+ }
+ body.y += tr.h;
+ body.h -= tr.h;
+ }
+
+ /* do `close` button */
+ if (~opt & MU_OPT_NOCLOSE) {
+ mu_Id id = mu_get_id(ctx, "!close", 6);
+ mu_Rect r = mu_rect(tr.x + tr.w - tr.h, tr.y, tr.h, tr.h);
+ tr.w -= r.w;
+ mu_draw_icon(ctx, MU_ICON_CLOSE, r, ctx->style->colors[MU_COLOR_TITLETEXT]);
+ mu_update_control(ctx, id, r, opt);
+ if (ctx->mouse_pressed == MU_MOUSE_LEFT && id == ctx->focus) {
+ cnt->open = 0;
+ }
+ }
+ }
+
+ push_container_body(ctx, cnt, body, opt);
+
+ /* do `resize` handle */
+ if (~opt & MU_OPT_NORESIZE) {
+ int sz = ctx->style->title_height;
+ mu_Id id = mu_get_id(ctx, "!resize", 7);
+ mu_Rect r = mu_rect(rect.x + rect.w - sz, rect.y + rect.h - sz, sz, sz);
+ mu_update_control(ctx, id, r, opt);
+ if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) {
+ cnt->rect.w = mu_max(96, cnt->rect.w + ctx->mouse_delta.x);
+ cnt->rect.h = mu_max(64, cnt->rect.h + ctx->mouse_delta.y);
+ }
+ }
+
+ /* resize to content size */
+ if (opt & MU_OPT_AUTOSIZE) {
+ mu_Rect r = get_layout(ctx)->body;
+ cnt->rect.w = cnt->content_size.x + (cnt->rect.w - r.w);
+ cnt->rect.h = cnt->content_size.y + (cnt->rect.h - r.h);
+ }
+
+ /* close if this is a popup window and elsewhere was clicked */
+ if (opt & MU_OPT_POPUP && ctx->mouse_pressed && ctx->hover_root != cnt) {
+ cnt->open = 0;
+ }
+
+ mu_push_clip_rect(ctx, cnt->body);
+ return MU_RES_ACTIVE;
+}
+
+
+void mu_end_window(mu_Context *ctx) {
+ mu_pop_clip_rect(ctx);
+ end_root_container(ctx);
+}
+
+
+void mu_open_popup(mu_Context *ctx, const char *name) {
+ mu_Container *cnt = mu_get_container(ctx, name);
+ /* set as hover root so popup isn't closed in begin_window_ex() */
+ ctx->hover_root = ctx->next_hover_root = cnt;
+ /* position at mouse cursor, open and bring-to-front */
+ cnt->rect = mu_rect(ctx->mouse_pos.x, ctx->mouse_pos.y, 1, 1);
+ cnt->open = 1;
+ mu_bring_to_front(ctx, cnt);
+}
+
+
+int mu_begin_popup(mu_Context *ctx, const char *name) {
+ int opt = MU_OPT_POPUP | MU_OPT_AUTOSIZE | MU_OPT_NORESIZE |
+ MU_OPT_NOSCROLL | MU_OPT_NOTITLE | MU_OPT_CLOSED;
+ return mu_begin_window_ex(ctx, name, mu_rect(0, 0, 0, 0), opt);
+}
+
+
+void mu_end_popup(mu_Context *ctx) {
+ mu_end_window(ctx);
+}
+
+
+void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt) {
+ mu_Container *cnt;
+ mu_push_id(ctx, name, strlen(name));
+ cnt = get_container(ctx, ctx->last_id, opt);
+ cnt->rect = mu_layout_next(ctx);
+ if (~opt & MU_OPT_NOFRAME) {
+ ctx->draw_frame(ctx, cnt->rect, MU_COLOR_PANELBG);
+ }
+ push(ctx->container_stack, cnt);
+ push_container_body(ctx, cnt, cnt->rect, opt);
+ mu_push_clip_rect(ctx, cnt->body);
+}
+
+
+void mu_end_panel(mu_Context *ctx) {
+ mu_pop_clip_rect(ctx);
+ pop_container(ctx);
+}
diff --git a/MicroUICVersion/microui.h b/MicroUICVersion/microui.h
new file mode 100644
index 0000000..6e18639
--- /dev/null
+++ b/MicroUICVersion/microui.h
@@ -0,0 +1,296 @@
+/*
+** Copyright (c) 2024 rxi
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of the MIT license. See `microui.c` for details.
+*/
+
+#ifndef MICROUI_H
+#define MICROUI_H
+
+#define MU_VERSION "2.02"
+
+#define MU_COMMANDLIST_SIZE (256 * 1024)
+#define MU_ROOTLIST_SIZE 32
+#define MU_CONTAINERSTACK_SIZE 32
+#define MU_CLIPSTACK_SIZE 32
+#define MU_IDSTACK_SIZE 32
+#define MU_LAYOUTSTACK_SIZE 16
+#define MU_CONTAINERPOOL_SIZE 48
+#define MU_TREENODEPOOL_SIZE 48
+#define MU_MAX_WIDTHS 16
+#define MU_REAL float
+#define MU_REAL_FMT "%.3g"
+#define MU_SLIDER_FMT "%.2f"
+#define MU_MAX_FMT 127
+
+#define mu_stack(T, n) struct { int idx; T items[n]; }
+#define mu_min(a, b) ((a) < (b) ? (a) : (b))
+#define mu_max(a, b) ((a) > (b) ? (a) : (b))
+#define mu_clamp(x, a, b) mu_min(b, mu_max(a, x))
+
+enum {
+ MU_CLIP_PART = 1,
+ MU_CLIP_ALL
+};
+
+enum {
+ MU_COMMAND_JUMP = 1,
+ MU_COMMAND_CLIP,
+ MU_COMMAND_RECT,
+ MU_COMMAND_TEXT,
+ MU_COMMAND_ICON,
+ MU_COMMAND_MAX
+};
+
+enum {
+ MU_COLOR_TEXT,
+ MU_COLOR_BORDER,
+ MU_COLOR_WINDOWBG,
+ MU_COLOR_TITLEBG,
+ MU_COLOR_TITLETEXT,
+ MU_COLOR_PANELBG,
+ MU_COLOR_BUTTON,
+ MU_COLOR_BUTTONHOVER,
+ MU_COLOR_BUTTONFOCUS,
+ MU_COLOR_BASE,
+ MU_COLOR_BASEHOVER,
+ MU_COLOR_BASEFOCUS,
+ MU_COLOR_SCROLLBASE,
+ MU_COLOR_SCROLLTHUMB,
+ MU_COLOR_MAX
+};
+
+enum {
+ MU_ICON_CLOSE = 1,
+ MU_ICON_CHECK,
+ MU_ICON_COLLAPSED,
+ MU_ICON_EXPANDED,
+ MU_ICON_MAX
+};
+
+enum {
+ MU_RES_ACTIVE = (1 << 0),
+ MU_RES_SUBMIT = (1 << 1),
+ MU_RES_CHANGE = (1 << 2)
+};
+
+enum {
+ MU_OPT_ALIGNCENTER = (1 << 0),
+ MU_OPT_ALIGNRIGHT = (1 << 1),
+ MU_OPT_NOINTERACT = (1 << 2),
+ MU_OPT_NOFRAME = (1 << 3),
+ MU_OPT_NORESIZE = (1 << 4),
+ MU_OPT_NOSCROLL = (1 << 5),
+ MU_OPT_NOCLOSE = (1 << 6),
+ MU_OPT_NOTITLE = (1 << 7),
+ MU_OPT_HOLDFOCUS = (1 << 8),
+ MU_OPT_AUTOSIZE = (1 << 9),
+ MU_OPT_POPUP = (1 << 10),
+ MU_OPT_CLOSED = (1 << 11),
+ MU_OPT_EXPANDED = (1 << 12)
+};
+
+enum {
+ MU_MOUSE_LEFT = (1 << 0),
+ MU_MOUSE_RIGHT = (1 << 1),
+ MU_MOUSE_MIDDLE = (1 << 2)
+};
+
+enum {
+ MU_KEY_SHIFT = (1 << 0),
+ MU_KEY_CTRL = (1 << 1),
+ MU_KEY_ALT = (1 << 2),
+ MU_KEY_BACKSPACE = (1 << 3),
+ MU_KEY_RETURN = (1 << 4)
+};
+
+
+typedef struct mu_Context mu_Context;
+typedef unsigned mu_Id;
+typedef MU_REAL mu_Real;
+typedef void* mu_Font;
+
+typedef struct { int x, y; } mu_Vec2;
+typedef struct { int x, y, w, h; } mu_Rect;
+typedef struct { unsigned char r, g, b, a; } mu_Color;
+typedef struct { mu_Id id; int last_update; } mu_PoolItem;
+
+typedef struct { int type, size; } mu_BaseCommand;
+typedef struct { mu_BaseCommand base; void *dst; } mu_JumpCommand;
+typedef struct { mu_BaseCommand base; mu_Rect rect; } mu_ClipCommand;
+typedef struct { mu_BaseCommand base; mu_Rect rect; mu_Color color; } mu_RectCommand;
+typedef struct { mu_BaseCommand base; mu_Font font; mu_Vec2 pos; mu_Color color; char str[1]; } mu_TextCommand;
+typedef struct { mu_BaseCommand base; mu_Rect rect; int id; mu_Color color; } mu_IconCommand;
+
+typedef union {
+ int type;
+ mu_BaseCommand base;
+ mu_JumpCommand jump;
+ mu_ClipCommand clip;
+ mu_RectCommand rect;
+ mu_TextCommand text;
+ mu_IconCommand icon;
+} mu_Command;
+
+typedef struct {
+ mu_Rect body;
+ mu_Rect next;
+ mu_Vec2 position;
+ mu_Vec2 size;
+ mu_Vec2 max;
+ int widths[MU_MAX_WIDTHS];
+ int items;
+ int item_index;
+ int next_row;
+ int next_type;
+ int indent;
+} mu_Layout;
+
+typedef struct {
+ mu_Command *head, *tail;
+ mu_Rect rect;
+ mu_Rect body;
+ mu_Vec2 content_size;
+ mu_Vec2 scroll;
+ int zindex;
+ int open;
+} mu_Container;
+
+typedef struct {
+ mu_Font font;
+ mu_Vec2 size;
+ int padding;
+ int spacing;
+ int indent;
+ int title_height;
+ int scrollbar_size;
+ int thumb_size;
+ mu_Color colors[MU_COLOR_MAX];
+} mu_Style;
+
+struct mu_Context {
+ /* callbacks */
+ int (*text_width)(mu_Font font, const char *str, int len);
+ int (*text_height)(mu_Font font);
+ void (*draw_frame)(mu_Context *ctx, mu_Rect rect, int colorid);
+ /* core state */
+ mu_Style _style;
+ mu_Style *style;
+ mu_Id hover;
+ mu_Id focus;
+ mu_Id last_id;
+ mu_Rect last_rect;
+ int last_zindex;
+ int updated_focus;
+ int frame;
+ mu_Container *hover_root;
+ mu_Container *next_hover_root;
+ mu_Container *scroll_target;
+ char number_edit_buf[MU_MAX_FMT];
+ mu_Id number_edit;
+ /* stacks */
+ mu_stack(char, MU_COMMANDLIST_SIZE) command_list;
+ mu_stack(mu_Container*, MU_ROOTLIST_SIZE) root_list;
+ mu_stack(mu_Container*, MU_CONTAINERSTACK_SIZE) container_stack;
+ mu_stack(mu_Rect, MU_CLIPSTACK_SIZE) clip_stack;
+ mu_stack(mu_Id, MU_IDSTACK_SIZE) id_stack;
+ mu_stack(mu_Layout, MU_LAYOUTSTACK_SIZE) layout_stack;
+ /* retained state pools */
+ mu_PoolItem container_pool[MU_CONTAINERPOOL_SIZE];
+ mu_Container containers[MU_CONTAINERPOOL_SIZE];
+ mu_PoolItem treenode_pool[MU_TREENODEPOOL_SIZE];
+ /* input state */
+ mu_Vec2 mouse_pos;
+ mu_Vec2 last_mouse_pos;
+ mu_Vec2 mouse_delta;
+ mu_Vec2 scroll_delta;
+ int mouse_down;
+ int mouse_pressed;
+ int key_down;
+ int key_pressed;
+ char input_text[32];
+};
+
+
+mu_Vec2 mu_vec2(int x, int y);
+mu_Rect mu_rect(int x, int y, int w, int h);
+mu_Color mu_color(int r, int g, int b, int a);
+
+void mu_init(mu_Context *ctx);
+void mu_begin(mu_Context *ctx);
+void mu_end(mu_Context *ctx);
+void mu_set_focus(mu_Context *ctx, mu_Id id);
+mu_Id mu_get_id(mu_Context *ctx, const void *data, int size);
+void mu_push_id(mu_Context *ctx, const void *data, int size);
+void mu_pop_id(mu_Context *ctx);
+void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect);
+void mu_pop_clip_rect(mu_Context *ctx);
+mu_Rect mu_get_clip_rect(mu_Context *ctx);
+int mu_check_clip(mu_Context *ctx, mu_Rect r);
+mu_Container* mu_get_current_container(mu_Context *ctx);
+mu_Container* mu_get_container(mu_Context *ctx, const char *name);
+void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt);
+
+int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id);
+int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id);
+void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx);
+
+void mu_input_mousemove(mu_Context *ctx, int x, int y);
+void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn);
+void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn);
+void mu_input_scroll(mu_Context *ctx, int x, int y);
+void mu_input_keydown(mu_Context *ctx, int key);
+void mu_input_keyup(mu_Context *ctx, int key);
+void mu_input_text(mu_Context *ctx, const char *text);
+
+mu_Command* mu_push_command(mu_Context *ctx, int type, int size);
+int mu_next_command(mu_Context *ctx, mu_Command **cmd);
+void mu_set_clip(mu_Context *ctx, mu_Rect rect);
+void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color);
+void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color);
+void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len, mu_Vec2 pos, mu_Color color);
+void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color);
+
+void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height);
+void mu_layout_width(mu_Context *ctx, int width);
+void mu_layout_height(mu_Context *ctx, int height);
+void mu_layout_begin_column(mu_Context *ctx);
+void mu_layout_end_column(mu_Context *ctx);
+void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative);
+mu_Rect mu_layout_next(mu_Context *ctx);
+
+void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect, int colorid, int opt);
+void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect, int colorid, int opt);
+int mu_mouse_over(mu_Context *ctx, mu_Rect rect);
+void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt);
+
+#define mu_button(ctx, label) mu_button_ex(ctx, label, 0, MU_OPT_ALIGNCENTER)
+#define mu_textbox(ctx, buf, bufsz) mu_textbox_ex(ctx, buf, bufsz, 0)
+#define mu_slider(ctx, value, lo, hi) mu_slider_ex(ctx, value, lo, hi, 0, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER)
+#define mu_number(ctx, value, step) mu_number_ex(ctx, value, step, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER)
+#define mu_header(ctx, label) mu_header_ex(ctx, label, 0)
+#define mu_begin_treenode(ctx, label) mu_begin_treenode_ex(ctx, label, 0)
+#define mu_begin_window(ctx, title, rect) mu_begin_window_ex(ctx, title, rect, 0)
+#define mu_begin_panel(ctx, name) mu_begin_panel_ex(ctx, name, 0)
+
+void mu_text(mu_Context *ctx, const char *text);
+void mu_label(mu_Context *ctx, const char *text);
+int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt);
+int mu_checkbox(mu_Context *ctx, const char *label, int *state);
+int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r, int opt);
+int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt);
+int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high, mu_Real step, const char *fmt, int opt);
+int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step, const char *fmt, int opt);
+int mu_header_ex(mu_Context *ctx, const char *label, int opt);
+int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt);
+void mu_end_treenode(mu_Context *ctx);
+int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt);
+void mu_end_window(mu_Context *ctx);
+void mu_open_popup(mu_Context *ctx, const char *name);
+int mu_begin_popup(mu_Context *ctx, const char *name);
+void mu_end_popup(mu_Context *ctx);
+void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt);
+void mu_end_panel(mu_Context *ctx);
+
+#endif