changeset 60:d64a8c189a77

Merged
author June Park <me@mrjunejune.com>
date Sat, 20 Dec 2025 13:56:01 -0500
parents 983769fba767 (current diff) e06bc03d9618 (diff)
children 9df5587cf23b
files
diffstat 32 files changed, 9324 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/MODULE.bazel.lock	Tue Dec 16 21:01:45 2025 -0500
+++ b/MODULE.bazel.lock	Sat Dec 20 13:56:01 2025 -0500
@@ -16,8 +16,7 @@
     "https://bcr.bazel.build/modules/abseil-py/2.1.0/source.json": "0e8fc4f088ce07099c1cd6594c20c7ddbb48b4b3c0849b7d94ba94be88ff042b",
     "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896",
     "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85",
-    "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442",
-    "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2",
+    "https://bcr.bazel.build/modules/apple_support/1.15.1/source.json": "517f2b77430084c541bc9be2db63fdcbb7102938c5f64c17ee60ffda2e5cf07b",
     "https://bcr.bazel.build/modules/bazel_features/1.1.0/MODULE.bazel": "cfd42ff3b815a5f39554d97182657f8c4b9719568eb7fded2b9135f084bf760b",
     "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
     "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
@@ -28,7 +27,6 @@
     "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
     "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
     "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a",
-    "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65",
     "https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
     "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
     "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
@@ -228,6 +226,37 @@
   },
   "selectedYankedVersions": {},
   "moduleExtensions": {
+    "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": {
+      "general": {
+        "bzlTransitiveDigest": "p7Ghcq3+nnQxCrf+U3xnhdn7yOSTDbcFyGHK7Ja+rU4=",
+        "usagesDigest": "EJfdUgbJdIRboG70S9dz8HhGVkL2/s3qFQ9xht8y2Rs=",
+        "recordedFileInputs": {},
+        "recordedDirentsInputs": {},
+        "envVariables": {},
+        "generatedRepoSpecs": {
+          "local_config_apple_cc_toolchains": {
+            "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains",
+            "attributes": {}
+          },
+          "local_config_apple_cc": {
+            "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf",
+            "attributes": {}
+          }
+        },
+        "recordedRepoMappingEntries": [
+          [
+            "apple_support+",
+            "bazel_tools",
+            "bazel_tools"
+          ],
+          [
+            "bazel_tools",
+            "rules_cc",
+            "rules_cc+"
+          ]
+        ]
+      }
+    },
     "@@rules_android+//bzlmod_extensions:apksig.bzl%apksig_extension": {
       "general": {
         "bzlTransitiveDigest": "oNxmR17oNBDtqdMtQ04f77G1dxpDmcW7KbgGb9I5vp0=",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/color_game/BUILD	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,11 @@
+load("//third_party/raylib:raylib.bzl", "raylib_binary")
+
+raylib_binary(
+  name = "main",
+  srcs = ["main.c"],
+  deps = [
+    "//third_party/raylib:raylib",
+    "//dowa:dowa",
+  ],
+  static = True
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/color_game/main.c	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,775 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "dowa/dowa.h"
+#include "third_party/raylib/include/raylib.h"
+
+#define INIT_SCREEN_WIDTH 1200
+#define INIT_SCREEN_HEIGHT 700
+#define PLAYER_SPEED 200.0f
+#define PLAYER_RADIUS 20.0f
+#define MONSTER_SPEED 100.0f
+#define MONSTER_RADIUS 15.0f
+#define BULLET_SPEED 300.0f
+#define BULLET_RADIUS 5.0f
+#define BULLET_LIFETIME 3.0f
+#define SHOOT_INTERVAL 0.5f
+#define HIT_PROBABILITY 0.7f  // 70% aim accuracy (0.0 = random, 1.0 = perfect)
+#define MAX_PASSIVE_NODES 12
+#define PASSIVE_TREE_RADIUS 250.0f
+#define PASSIVE_NODE_RADIUS 20.0f
+#define BASE_DAMAGE 10.0f
+#define PLAYER_MAX_HEALTH 100.0f
+#define MONSTER_CONTACT_DAMAGE 5.0f
+#define BOSS_HEALTH_MULTIPLIER 5.0f
+#define COLOR_UNLOCK_TIME 30.0f
+#define BOSS_SPAWN_TIME 60.0f
+
+typedef struct Player {
+  Vector2 position;
+  Color color;
+  float health;
+  float maxHealth;
+  int unlockedBulletTypes[MAX_PASSIVE_NODES];
+  int unlockedCount;
+  int passivePoints;
+  float invulnerabilityTimer;
+} Player;
+
+typedef struct Map {
+  float width;
+  float height;
+} Map;
+
+typedef struct Monster {
+  Vector2 position;
+  float hue;
+  float saturation;
+  float health;
+  float maxHealth;
+  bool alive;
+  bool hasCollision;
+  bool isBoss;
+} Monster;
+
+typedef struct Bullet {
+  Vector2 position;
+  Vector2 velocity;
+  float lifetime;
+  float hue;
+  float damage;
+  bool active;
+} Bullet;
+
+typedef struct PassiveNode {
+  float angle;
+  float hue;
+  char description[64];
+  float damageBonus;
+  bool unlocked;
+} PassiveNode;
+
+float RandomFloat(float min, float max)
+{
+  return min + ((float)rand() / (float)RAND_MAX) * (max - min);
+}
+
+float CalculateColorDamage(float bulletHue, float monsterHue, float baseDamage)
+{
+  // Calculate hue difference (0-180 degrees)
+  float diff = fabs(bulletHue - monsterHue);
+  if (diff > 180.0f) diff = 360.0f - diff;
+
+  // Opposite colors (180 degrees) = 2x damage
+  // Same color (0 degrees) = 0.5x damage
+  // Linear scale between them
+  float damageMultiplier = 0.5f + (diff / 180.0f) * 1.5f;
+
+  return baseDamage * damageMultiplier;
+}
+
+void InitializePassiveTree(PassiveNode* nodes)
+{
+  const char* nodeDescriptions[MAX_PASSIVE_NODES] = {
+    "Red Bullet",      // 0°
+    "Orange Bullet",   // 30°
+    "Yellow Bullet",   // 60°
+    "Chartreuse",      // 90°
+    "Green Bullet",    // 120°
+    "Spring Green",    // 150°
+    "Cyan Bullet",     // 180°
+    "Azure Bullet",    // 210°
+    "Blue Bullet",     // 240°
+    "Violet Bullet",   // 270°
+    "Magenta Bullet",  // 300°
+    "Rose Bullet"      // 330°
+  };
+
+  for (int i = 0; i < MAX_PASSIVE_NODES; i++)
+  {
+    nodes[i].angle = (360.0f / MAX_PASSIVE_NODES) * i;
+    nodes[i].hue = nodes[i].angle;
+    snprintf(nodes[i].description, sizeof(nodes[i].description), "%s", nodeDescriptions[i]);
+    nodes[i].damageBonus = 5.0f + (i * 2.0f); // Scaling damage bonus
+    nodes[i].unlocked = false;
+  }
+}
+
+void HandlePlayerMovement(Player* player, Map* map, float deltaTime)
+{
+  Vector2 movement = {0};
+
+  if (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))    movement.y -= 1.0f;
+  if (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))  movement.y += 1.0f;
+  if (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))  movement.x -= 1.0f;
+  if (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) movement.x += 1.0f;
+
+  // Normalize diagonal movement
+  float length = sqrtf(movement.x * movement.x + movement.y * movement.y);
+  if (length > 0)
+  {
+    movement.x /= length;
+    movement.y /= length;
+  }
+
+  // Apply movement
+  player->position.x += movement.x * PLAYER_SPEED * deltaTime;
+  player->position.y += movement.y * PLAYER_SPEED * deltaTime;
+
+  // Keep player within map bounds
+  if (player->position.x < PLAYER_RADIUS) player->position.x = PLAYER_RADIUS;
+  if (player->position.x > map->width - PLAYER_RADIUS)
+    player->position.x = map->width - PLAYER_RADIUS;
+  if (player->position.y < PLAYER_RADIUS) player->position.y = PLAYER_RADIUS;
+  if (player->position.y > map->height - PLAYER_RADIUS)
+    player->position.y = map->height - PLAYER_RADIUS;
+}
+
+void UpdateMonsters(Monster* monsters, int monsterCount, Vector2 playerPos, float deltaTime)
+{
+  for (int i = 0; i < monsterCount; i++)
+  {
+    if (!monsters[i].alive) continue;
+
+    // Calculate direction to player
+    Vector2 direction = {
+      playerPos.x - monsters[i].position.x,
+      playerPos.y - monsters[i].position.y
+    };
+
+    // Normalize direction
+    float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
+    if (length > 0)
+    {
+      direction.x /= length;
+      direction.y /= length;
+    }
+
+    // Move toward player
+    Vector2 newPos = {
+      monsters[i].position.x + direction.x * MONSTER_SPEED * deltaTime,
+      monsters[i].position.y + direction.y * MONSTER_SPEED * deltaTime
+    };
+
+    // Check collision with other monsters if this monster has collision enabled
+    if (monsters[i].hasCollision)
+    {
+      bool collision = false;
+      for (int j = 0; j < monsterCount; j++)
+      {
+        if (i == j || !monsters[j].alive || !monsters[j].hasCollision) continue;
+
+        float dx = newPos.x - monsters[j].position.x;
+        float dy = newPos.y - monsters[j].position.y;
+        float distance = sqrtf(dx * dx + dy * dy);
+
+        if (distance < MONSTER_RADIUS * 2)
+        {
+          collision = true;
+          break;
+        }
+      }
+
+      if (!collision)
+      {
+        monsters[i].position = newPos;
+      }
+    }
+    else
+    {
+      monsters[i].position = newPos;
+    }
+  }
+}
+
+void CheckPlayerMonsterCollision(Player* player, Monster* monsters, int monsterCount, float deltaTime)
+{
+  if (player->invulnerabilityTimer > 0)
+  {
+    player->invulnerabilityTimer -= deltaTime;
+    return;
+  }
+
+  for (int i = 0; i < monsterCount; i++)
+  {
+    if (!monsters[i].alive) continue;
+
+    float dx = player->position.x - monsters[i].position.x;
+    float dy = player->position.y - monsters[i].position.y;
+    float distance = sqrtf(dx * dx + dy * dy);
+
+    if (distance < PLAYER_RADIUS + MONSTER_RADIUS)
+    {
+      player->health -= MONSTER_CONTACT_DAMAGE;
+      player->invulnerabilityTimer = 0.5f; // 0.5 second invulnerability
+      if (player->health < 0) player->health = 0;
+      break;
+    }
+  }
+}
+
+void UpdateBullets(Bullet* bullets, int bulletCount, float deltaTime)
+{
+  for (int i = 0; i < bulletCount; i++)
+  {
+    if (!bullets[i].active) continue;
+
+    // Update position
+    bullets[i].position.x += bullets[i].velocity.x * deltaTime;
+    bullets[i].position.y += bullets[i].velocity.y * deltaTime;
+
+    // Update lifetime
+    bullets[i].lifetime -= deltaTime;
+    if (bullets[i].lifetime <= 0)
+    {
+      bullets[i].active = false;
+    }
+  }
+}
+
+int CheckCollisions(Bullet* bullets, int bulletCount, Monster* monsters, int monsterCount, Player* player)
+{
+  int bossesKilled = 0;
+
+  for (int i = 0; i < bulletCount; i++)
+  {
+    if (!bullets[i].active) continue;
+
+    for (int j = 0; j < monsterCount; j++)
+    {
+      if (!monsters[j].alive) continue;
+
+      // Check distance between bullet and monster
+      float dx = bullets[i].position.x - monsters[j].position.x;
+      float dy = bullets[i].position.y - monsters[j].position.y;
+      float distance = sqrtf(dx * dx + dy * dy);
+
+      if (distance < BULLET_RADIUS + MONSTER_RADIUS)
+      {
+        // Calculate damage based on color difference
+        float damage = CalculateColorDamage(bullets[i].hue, monsters[j].hue, bullets[i].damage);
+        monsters[j].health -= damage;
+
+        // Check if monster died
+        if (monsters[j].health <= 0)
+        {
+          monsters[j].alive = false;
+
+          // Award passive point if boss was killed
+          if (monsters[j].isBoss)
+          {
+            player->passivePoints++;
+            bossesKilled++;
+          }
+        }
+
+        bullets[i].active = false;
+        break;
+      }
+    }
+  }
+
+  return bossesKilled;
+}
+
+Vector2 FindNearestMonster(Monster* monsters, int monsterCount, Vector2 playerPos)
+{
+  float minDistance = INFINITY;
+  Vector2 nearestPos = playerPos;
+  bool found = false;
+
+  for (int i = 0; i < monsterCount; i++)
+  {
+    if (!monsters[i].alive) continue;
+
+    float dx = monsters[i].position.x - playerPos.x;
+    float dy = monsters[i].position.y - playerPos.y;
+    float distance = sqrtf(dx * dx + dy * dy);
+
+    if (distance < minDistance)
+    {
+      minDistance = distance;
+      nearestPos = monsters[i].position;
+      found = true;
+    }
+  }
+
+  if (!found)
+  {
+    // No monsters, return random direction
+    float angle = RandomFloat(0.0f, 2.0f * PI);
+    return (Vector2){
+      playerPos.x + cosf(angle) * 100.0f,
+      playerPos.y + sinf(angle) * 100.0f
+    };
+  }
+
+  return nearestPos;
+}
+
+int main()
+{
+  InitWindow(INIT_SCREEN_WIDTH, INIT_SCREEN_HEIGHT, "color game");
+  SetTargetFPS(60);
+  srand(time(NULL));
+
+  // --- All Global Variables ---
+  Map map = {
+    .width = INIT_SCREEN_WIDTH * 3,
+    .height = INIT_SCREEN_HEIGHT * 3
+  };
+
+  // Initialize player at map center
+  Player player = {
+    .position = {map.width / 2, map.height / 2},
+    .color = BLUE,
+    .health = PLAYER_MAX_HEALTH,
+    .maxHealth = PLAYER_MAX_HEALTH,
+    .unlockedCount = 0,
+    .passivePoints = 0,
+    .invulnerabilityTimer = 0.0f
+  };
+
+  // Initialize passive tree
+  PassiveNode passiveTree[MAX_PASSIVE_NODES];
+  InitializePassiveTree(passiveTree);
+
+  // Unlock first node only (grayscale bullet to start)
+  passiveTree[0].unlocked = true;
+  player.unlockedBulletTypes[player.unlockedCount++] = 0;
+
+  // Initialize camera
+  Camera2D camera = {0};
+  camera.target = player.position;
+  camera.offset = (Vector2){INIT_SCREEN_WIDTH / 2.0f, INIT_SCREEN_HEIGHT / 2.0f};
+  camera.rotation = 0.0f;
+  camera.zoom = 1.0f;
+
+  bool menuOpen = false;
+  bool showMonsterColors = false;
+
+  Vector2 menuCenter = {
+    .x = INIT_SCREEN_WIDTH / 2,
+    .y = INIT_SCREEN_HEIGHT / 2
+  };
+
+  // Game progression variables
+  float gameTime = 0.0f;
+  float currentMonsterHue = 0.0f;
+  float currentMonsterSaturation = 0.0f; // Start grayscale
+  int bossesDefeated = 0;
+  float nextBossTime = BOSS_SPAWN_TIME;
+  bool colorUnlocked = false;
+
+  // Initialize monsters array
+  #define MAX_MONSTERS 100
+  Monster live_monsters[MAX_MONSTERS];
+  int monsterCount = 0;
+
+  // Spawn initial monsters (grayscale)
+  for (int i = 0; i < 10; i++)
+  {
+    live_monsters[monsterCount++] = (Monster){
+      .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
+      .hue = currentMonsterHue,
+      .saturation = currentMonsterSaturation,
+      .health = 50.0f,
+      .maxHealth = 50.0f,
+      .alive = true,
+      .hasCollision = true,
+      .isBoss = false
+    };
+  }
+
+  // Initialize bullets array
+  #define MAX_BULLETS 200
+  Bullet bullets[MAX_BULLETS];
+  int bulletCount = 0;
+  for (int i = 0; i < MAX_BULLETS; i++)
+  {
+    bullets[i].active = false;
+  }
+
+  // Shooting timer
+  float shootTimer = 0.0f;
+  float hitProbability = HIT_PROBABILITY;
+
+  while (!WindowShouldClose())
+  {
+    float deltaTime = GetFrameTime();
+
+    if (IsKeyPressed(KEY_P))
+    {
+      menuOpen = !menuOpen;
+    }
+
+    if (IsKeyPressed(KEY_R))
+    {
+      showMonsterColors = !showMonsterColors;
+    }
+
+    // Handle passive tree menu interactions
+    if (menuOpen)
+    {
+      if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+      {
+        Vector2 mousePos = GetMousePosition();
+
+        // Check if clicked on a passive node
+        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
+        {
+          float angleRad = passiveTree[i].angle * DEG2RAD;
+          Vector2 nodePos = {
+            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
+            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
+          };
+
+          float dx = mousePos.x - nodePos.x;
+          float dy = mousePos.y - nodePos.y;
+          float distance = sqrtf(dx * dx + dy * dy);
+
+          if (distance < PASSIVE_NODE_RADIUS && !passiveTree[i].unlocked && player.passivePoints > 0)
+          {
+            // Unlock the node
+            passiveTree[i].unlocked = true;
+            player.unlockedBulletTypes[player.unlockedCount++] = i;
+            player.passivePoints--;
+            break;
+          }
+        }
+      }
+    }
+
+    // Only handle game logic when menu is closed
+    if (!menuOpen)
+    {
+      // Update game time and progression
+      gameTime += deltaTime;
+
+      // Unlock color after COLOR_UNLOCK_TIME
+      if (!colorUnlocked && gameTime >= COLOR_UNLOCK_TIME)
+      {
+        colorUnlocked = true;
+        currentMonsterSaturation = 1.0f;
+      }
+
+      // Spawn boss and unlock new color
+      if (gameTime >= nextBossTime && monsterCount < MAX_MONSTERS)
+      {
+        // Generate new color for this boss cycle
+        currentMonsterHue = RandomFloat(0, 360);
+        nextBossTime += BOSS_SPAWN_TIME;
+
+        // Spawn boss monster
+        live_monsters[monsterCount++] = (Monster){
+          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
+          .hue = currentMonsterHue,
+          .saturation = currentMonsterSaturation,
+          .health = 50.0f * BOSS_HEALTH_MULTIPLIER,
+          .maxHealth = 50.0f * BOSS_HEALTH_MULTIPLIER,
+          .alive = true,
+          .hasCollision = true,
+          .isBoss = true
+        };
+      }
+
+      // Spawn regular monsters periodically
+      static float monsterSpawnTimer = 0.0f;
+      monsterSpawnTimer += deltaTime;
+      if (monsterSpawnTimer >= 3.0f && monsterCount < MAX_MONSTERS)
+      {
+        monsterSpawnTimer = 0.0f;
+        live_monsters[monsterCount++] = (Monster){
+          .position = {RandomFloat(0, map.width), RandomFloat(0, map.height)},
+          .hue = currentMonsterHue,
+          .saturation = currentMonsterSaturation,
+          .health = 50.0f,
+          .maxHealth = 50.0f,
+          .alive = true,
+          .hasCollision = true,
+          .isBoss = false
+        };
+      }
+
+      HandlePlayerMovement(&player, &map, deltaTime);
+
+      // Check player-monster collision
+      CheckPlayerMonsterCollision(&player, live_monsters, monsterCount, deltaTime);
+
+      // Update shoot timer and shoot bullets
+      shootTimer += deltaTime;
+      if (shootTimer >= SHOOT_INTERVAL && player.unlockedCount > 0)
+      {
+        shootTimer = 0.0f;
+
+        // Find inactive bullet slot
+        for (int i = 0; i < MAX_BULLETS; i++)
+        {
+          if (!bullets[i].active)
+          {
+            // Choose random bullet type from unlocked types
+            int randomIndex = (int)RandomFloat(0, player.unlockedCount);
+            int nodeIndex = player.unlockedBulletTypes[randomIndex];
+            PassiveNode* selectedNode = &passiveTree[nodeIndex];
+
+            // Find nearest monster to aim at
+            Vector2 targetPos = FindNearestMonster(live_monsters, monsterCount, player.position);
+
+            // Calculate direction to target
+            Vector2 direction = {
+              targetPos.x - player.position.x,
+              targetPos.y - player.position.y
+            };
+
+            // Normalize
+            float length = sqrtf(direction.x * direction.x + direction.y * direction.y);
+            if (length > 0)
+            {
+              direction.x /= length;
+              direction.y /= length;
+            }
+
+            // Calculate base angle
+            float baseAngle = atan2f(direction.y, direction.x);
+
+            // Add random deviation based on accuracy
+            float maxDeviation = (1.0f - hitProbability) * PI;
+            float deviation = RandomFloat(-maxDeviation, maxDeviation);
+            float finalAngle = baseAngle + deviation;
+
+            bullets[i] = (Bullet){
+              .position = player.position,
+              .velocity = {
+                cosf(finalAngle) * BULLET_SPEED,
+                sinf(finalAngle) * BULLET_SPEED
+              },
+              .lifetime = BULLET_LIFETIME,
+              .hue = selectedNode->hue,
+              .damage = BASE_DAMAGE + selectedNode->damageBonus,
+              .active = true
+            };
+            break;
+          }
+        }
+      }
+
+      // Update monsters
+      UpdateMonsters(live_monsters, monsterCount, player.position, deltaTime);
+
+      // Update bullets
+      UpdateBullets(bullets, MAX_BULLETS, deltaTime);
+
+      // Check collisions
+      int bossKills = CheckCollisions(bullets, MAX_BULLETS, live_monsters, monsterCount, &player);
+      bossesDefeated += bossKills;
+
+      // Update camera to follow player
+      camera.target = player.position;
+
+      // Clamp camera to map edges
+      float minX = INIT_SCREEN_WIDTH / 2.0f;
+      float maxX = map.width - INIT_SCREEN_WIDTH / 2.0f;
+      float minY = INIT_SCREEN_HEIGHT / 2.0f;
+      float maxY = map.height - INIT_SCREEN_HEIGHT / 2.0f;
+
+      if (camera.target.x < minX) camera.target.x = minX;
+      if (camera.target.x > maxX) camera.target.x = maxX;
+      if (camera.target.y < minY) camera.target.y = minY;
+      if (camera.target.y > maxY) camera.target.y = maxY;
+    }
+
+    // --- Drawings ---
+    BeginDrawing();
+      ClearBackground(RAYWHITE);
+
+      if (menuOpen)
+      {
+        // Draw color wheel background
+        for (int i = 0; i < 360; i++)
+        {
+          Color color = ColorFromHSV((float)i, 1.0f, 1.0f);
+          DrawCircleSector(
+            menuCenter,
+            PASSIVE_TREE_RADIUS + 30.0f,
+            (float)i,
+            (float)(i + 1),
+            1,
+            color
+          );
+        }
+
+        // Draw passive tree nodes
+        for (int i = 0; i < MAX_PASSIVE_NODES; i++)
+        {
+          float angleRad = passiveTree[i].angle * DEG2RAD;
+          Vector2 nodePos = {
+            menuCenter.x + cosf(angleRad) * PASSIVE_TREE_RADIUS,
+            menuCenter.y + sinf(angleRad) * PASSIVE_TREE_RADIUS
+          };
+
+          Color nodeColor = ColorFromHSV(passiveTree[i].hue, 1.0f, 1.0f);
+
+          if (passiveTree[i].unlocked)
+          {
+            // Draw unlocked node
+            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, nodeColor);
+            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, WHITE);
+            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 6, nodeColor);
+          }
+          else
+          {
+            // Draw locked node
+            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS, DARKGRAY);
+            DrawCircleV(nodePos, PASSIVE_NODE_RADIUS - 3, GRAY);
+          }
+
+          // Draw node info on hover
+          Vector2 mousePos = GetMousePosition();
+          float dx = mousePos.x - nodePos.x;
+          float dy = mousePos.y - nodePos.y;
+          float distance = sqrtf(dx * dx + dy * dy);
+
+          if (distance < PASSIVE_NODE_RADIUS)
+          {
+            DrawText(passiveTree[i].description, nodePos.x - 50, nodePos.y - 40, 12, BLACK);
+            DrawText(TextFormat("+%.0f dmg", passiveTree[i].damageBonus), nodePos.x - 30, nodePos.y - 28, 10, DARKGRAY);
+          }
+        }
+
+        // Draw menu instructions
+        DrawText("PASSIVE TREE MENU", menuCenter.x - 100, menuCenter.y - 350, 20, BLACK);
+        DrawText("Click nodes to unlock bullet types", menuCenter.x - 120, menuCenter.y - 330, 16, DARKGRAY);
+        DrawText(TextFormat("Passive Points Available: %d", player.passivePoints), menuCenter.x - 120, menuCenter.y - 310, 16, PURPLE);
+        DrawText("Defeat bosses to earn passive points!", menuCenter.x - 120, menuCenter.y - 290, 14, DARKGRAY);
+        DrawText("Press P to close", menuCenter.x - 70, menuCenter.y + 320, 18, BLACK);
+      }
+      else
+      {
+        // Draw game world (with camera)
+        BeginMode2D(camera);
+
+          // Draw map background
+          DrawRectangle(0, 0, map.width, map.height, (Color){240, 240, 240, 255});
+
+          // Draw map border
+          DrawRectangleLinesEx((Rectangle){0, 0, map.width, map.height}, 5.0f, DARKGRAY);
+
+          // Draw grid to show map scale
+          for (int i = 0; i <= map.width; i += 100)
+          {
+            DrawLine(i, 0, i, map.height, LIGHTGRAY);
+          }
+          for (int i = 0; i <= map.height; i += 100)
+          {
+            DrawLine(0, i, map.width, i, LIGHTGRAY);
+          }
+
+          // Draw monsters
+          for (int i = 0; i < monsterCount; i++)
+          {
+            if (live_monsters[i].alive)
+            {
+              Color monsterColor = ColorFromHSV(live_monsters[i].hue, live_monsters[i].saturation, 1.0f);
+              float radius = live_monsters[i].isBoss ? MONSTER_RADIUS * 2 : MONSTER_RADIUS;
+
+              DrawCircleV(live_monsters[i].position, radius, monsterColor);
+
+              // Draw boss crown indicator
+              if (live_monsters[i].isBoss)
+              {
+                DrawCircleV(live_monsters[i].position, radius - 5, YELLOW);
+                DrawCircleV(live_monsters[i].position, radius - 10, monsterColor);
+              }
+
+              // Draw health bar
+              float healthPercent = live_monsters[i].health / live_monsters[i].maxHealth;
+              Vector2 barPos = {live_monsters[i].position.x - 15, live_monsters[i].position.y - radius - 10};
+              DrawRectangle(barPos.x, barPos.y, 30, 4, DARKGRAY);
+              DrawRectangle(barPos.x, barPos.y, 30 * healthPercent, 4, GREEN);
+
+              // Draw color label if toggle is on
+              if (showMonsterColors)
+              {
+                char colorText[32];
+                snprintf(colorText, sizeof(colorText), "H:%.0f S:%.1f", live_monsters[i].hue, live_monsters[i].saturation);
+                DrawText(colorText, live_monsters[i].position.x - 25, live_monsters[i].position.y + radius + 5, 10, BLACK);
+              }
+            }
+          }
+
+          // Draw bullets
+          for (int i = 0; i < MAX_BULLETS; i++)
+          {
+            if (bullets[i].active)
+            {
+              Color bulletColor = ColorFromHSV(bullets[i].hue, 1.0f, 1.0f);
+              DrawCircleV(bullets[i].position, BULLET_RADIUS, bulletColor);
+            }
+          }
+
+          // Draw player (flash when invulnerable)
+          if (player.invulnerabilityTimer <= 0 || ((int)(player.invulnerabilityTimer * 10) % 2 == 0))
+          {
+            DrawCircleV(player.position, PLAYER_RADIUS, player.color);
+          }
+
+        EndMode2D();
+
+        // Draw UI (screen space)
+        // Timer at top center
+        int minutes = (int)(gameTime / 60);
+        int seconds = (int)gameTime % 60;
+        DrawText(TextFormat("Time: %02d:%02d", minutes, seconds), INIT_SCREEN_WIDTH / 2 - 60, 10, 30, BLACK);
+
+        // Player health bar
+        float healthPercent = player.health / player.maxHealth;
+        DrawRectangle(10, 10, 200, 20, DARKGRAY);
+        DrawRectangle(10, 10, 200 * healthPercent, 20, RED);
+        DrawText(TextFormat("HP: %.0f/%.0f", player.health, player.maxHealth), 15, 12, 16, WHITE);
+
+        // Passive points
+        DrawText(TextFormat("Passive Points: %d", player.passivePoints), 10, 40, 20, PURPLE);
+
+        // Controls
+        DrawText("WASD: Move | P: Menu | R: Toggle Colors", 10, 70, 16, DARKGRAY);
+
+        // Count alive monsters and bosses
+        int aliveCount = 0;
+        int bossCount = 0;
+        for (int i = 0; i < monsterCount; i++)
+        {
+          if (live_monsters[i].alive)
+          {
+            aliveCount++;
+            if (live_monsters[i].isBoss) bossCount++;
+          }
+        }
+        DrawText(TextFormat("Monsters: %d | Bosses: %d", aliveCount, bossCount), 10, 95, 18, DARKGRAY);
+        DrawText(TextFormat("Unlocked bullets: %d/%d", player.unlockedCount, MAX_PASSIVE_NODES), 10, 120, 16, DARKGRAY);
+        DrawText(TextFormat("Bosses defeated: %d", bossesDefeated), 10, 145, 16, DARKGRAY);
+      }
+
+    EndDrawing();
+  }
+  return 0;
+}
--- a/dowa/d_memory.c	Tue Dec 16 21:01:45 2025 -0500
+++ b/dowa/d_memory.c	Sat Dec 20 13:56:01 2025 -0500
@@ -10,6 +10,12 @@
     return NULL;
   }
   p_arena->buffer = malloc(capacity);
+  if (p_arena->buffer == NULL)
+  {
+    perror("malloc");
+    Dowa_Free(p_arena);
+    return NULL;
+  }
   p_arena->offset = 0;
   p_arena->capacity = capacity;
   return p_arena;
@@ -17,6 +23,9 @@
 
 void *Dowa_Arena_Allocate(Dowa_PArena p_arena, size_t size)
 {
+  if (!p_arena || !p_arena->buffer || size == 0)
+    return NULL;
+
   if (p_arena->offset + size > p_arena->capacity)
   {
     return NULL;
@@ -48,6 +57,24 @@
   return dest;
 }
 
+void Dowa_Arena_Reset(Dowa_PArena p_arena)
+{
+  if (!p_arena) return;
+  p_arena->offset = 0;
+}
+
+size_t Dowa_Arena_Get_Used(Dowa_PArena p_arena)
+{
+  if (!p_arena) return 0;
+  return p_arena->offset;
+}
+
+size_t Dowa_Arena_Get_Remaining(Dowa_PArena p_arena)
+{
+  if (!p_arena) return 0;
+  return p_arena->capacity - p_arena->offset;
+}
+
 // --- HashMap --- //
 Dowa_PHashMap Dowa_HashMap_Create(size_t capacity)
 {
@@ -84,12 +111,11 @@
     return NULL;
   }
   p_hash_map->entries = Dowa_Arena_Allocate(p_arena, sizeof(*p_hash_map->entries) * capacity);
-  memset(p_hash_map->entries, 0, capacity * sizeof *p_hash_map->entries);
   if (p_hash_map->entries == NULL)
   {
-    Dowa_Free(p_hash_map);
     return NULL;
   }
+  memset(p_hash_map->entries, 0, capacity * sizeof *p_hash_map->entries);
   p_hash_map->capacity = capacity;
   p_hash_map->current_capacity = 0;
   p_hash_map->p_arena = p_arena;
@@ -109,16 +135,19 @@
   else
   {
     Dowa_PHashEntry entry;
+    Dowa_PHashEntry next;
     if (p_hash_map->entries)
     {
       for (int idx=0; idx<p_hash_map->capacity; idx++)
       {
         entry = p_hash_map->entries[idx];
-        if (entry)
+        while (entry)
         {
+          next = entry->next;
           Dowa_Free(entry->key);
           Dowa_Free(entry->buffer);
           Dowa_Free(entry);
+          entry = next;
         }
       }
     }
@@ -129,6 +158,9 @@
 
 int32 Dowa_HashMap_Get_Position(Dowa_PHashMap p_hash_map, const char *key)
 {
+  if (!p_hash_map || !key)
+    return -1;
+
   int32 hash_val = HASH_KEY_NUMBER;
   int32 c;
   while ((c = *key++))
@@ -140,6 +172,9 @@
 
 void *Dowa_HashMap_Get(Dowa_PHashMap p_hash_map, const char *key)
 {
+  if (!p_hash_map || !key)
+    return NULL;
+
   int idx = Dowa_HashMap_Get_Position(p_hash_map, key);
   Dowa_PHashEntry entry = p_hash_map->entries[idx];
 
@@ -156,22 +191,27 @@
 }
 
 
-int32 Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_PHashMap p_hash_map, const char *key, 
+int32 Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_PHashMap p_hash_map, const char *key,
                                                void *value, size_t value_size,
                                                Dowa_HashMap_ValueType type)
 {
+  if (!p_hash_map || !key || !value)
+    return -1;
+
   int idx = Dowa_HashMap_Get_Position(p_hash_map, key);
   Dowa_PHashEntry entry = p_hash_map->entries[idx];
   Dowa_PHashEntry prev = NULL;
 
-  // Old key 
+  // Old key
   while (entry)
   {
     if (strcmp(entry->key, key) == 0)
     {
       // Fails if it the key exists...
-      return -1; 
+      return -1;
     }
+    prev = entry;
+    entry = entry->next;
   }
 
   // Overriding doesn't really make sense? when copying over
@@ -186,21 +226,27 @@
   //    entry->buffer = value;
   //    entry->capacity = value_size;
   //    entry->type = type;
-  //    return 0; 
+  //    return 0;
   //  }
   //  prev = entry;
   //  entry = entry->next;
   //}
 
-  // New Key 
-  entry = p_hash_map->p_arena ? 
+  // New Key
+  entry = p_hash_map->p_arena ?
     Dowa_Arena_Allocate(p_hash_map->p_arena, sizeof(Dowa_HashEntry)) :
     malloc(sizeof(Dowa_HashEntry));
   if (!entry) { perror("malloc or arena alloc"); return -1; }
 
   entry->key = p_hash_map->p_arena ?
-    Dowa_Arena_Copy(p_hash_map->p_arena, key, strlen(key) + 1 /* \0 */) : 
+    Dowa_Arena_Copy(p_hash_map->p_arena, key, strlen(key) + 1 /* \0 */) :
     strdup(key);
+  if (!entry->key)
+  {
+    perror("strdup or arena copy");
+    if (!p_hash_map->p_arena) Dowa_Free(entry);
+    return -1;
+  }
   entry->buffer = value;
   entry->capacity = value_size;
   entry->type = type;
@@ -215,10 +261,13 @@
   return 0;
 }
 
-int32 Dowa_HashMap_Push_Value_With_Type(Dowa_PHashMap p_hash_map, const char *key, 
+int32 Dowa_HashMap_Push_Value_With_Type(Dowa_PHashMap p_hash_map, const char *key,
                                         void *value, size_t value_size,
                                         Dowa_HashMap_ValueType type)
 {
+  if (!p_hash_map || !key || !value)
+    return -1;
+
   int idx = Dowa_HashMap_Get_Position(p_hash_map, key);
   Dowa_PHashEntry entry = p_hash_map->entries[idx];
   Dowa_PHashEntry prev = NULL;
@@ -282,23 +331,103 @@
 
 void Dowa_HashMap_Pop_Key(Dowa_PHashMap p_hash_map, const char *key)
 {
+  if (!p_hash_map || !key)
+    return;
+
+  int idx = Dowa_HashMap_Get_Position(p_hash_map, key);
+  Dowa_PHashEntry entry = p_hash_map->entries[idx];
+  Dowa_PHashEntry prev = NULL;
+
+  while (entry)
+  {
+    if (strcmp(entry->key, key) == 0)
+    {
+      if (prev)
+        prev->next = entry->next;
+      else
+        p_hash_map->entries[idx] = entry->next;
+
+      if (!(p_hash_map->p_arena))
+      {
+        Dowa_Free(entry->key);
+        Dowa_Free(entry->buffer);
+        Dowa_Free(entry);
+      }
+
+      if (p_hash_map->current_capacity > 0)
+        p_hash_map->current_capacity--;
+      return;
+    }
+    prev = entry;
+    entry = entry->next;
+  }
+}
+
+boolean Dowa_HashMap_Has_Key(Dowa_PHashMap p_hash_map, const char *key)
+{
+  if (!p_hash_map || !key)
+    return FALSE;
+
   int idx = Dowa_HashMap_Get_Position(p_hash_map, key);
   Dowa_PHashEntry entry = p_hash_map->entries[idx];
-  if (entry && !(p_hash_map->p_arena))
+
+  while (entry)
   {
-    Dowa_Free(entry->key);
-    Dowa_Free(entry->buffer);
-    Dowa_Free(entry);
+    if (strcmp(entry->key, key) == 0)
+      return TRUE;
+    entry = entry->next;
+  }
+
+  return FALSE;
+}
+
+void Dowa_HashMap_Clear(Dowa_PHashMap p_hash_map)
+{
+  if (!p_hash_map) return;
+
+  if (p_hash_map->p_arena)
+  {
+    for (int idx=0; idx<p_hash_map->capacity; idx++)
+      p_hash_map->entries[idx] = NULL;
   }
-  p_hash_map->entries[idx] = NULL;
-  if (p_hash_map->current_capacity > 0)
+  else
   {
-    p_hash_map->current_capacity--;
+    Dowa_PHashEntry entry;
+    Dowa_PHashEntry next;
+    if (p_hash_map->entries)
+    {
+      for (int idx=0; idx<p_hash_map->capacity; idx++)
+      {
+        entry = p_hash_map->entries[idx];
+        while (entry)
+        {
+          next = entry->next;
+          Dowa_Free(entry->key);
+          Dowa_Free(entry->buffer);
+          Dowa_Free(entry);
+          entry = next;
+        }
+        p_hash_map->entries[idx] = NULL;
+      }
+    }
   }
+  p_hash_map->current_capacity = 0;
+}
+
+uint32 Dowa_HashMap_Get_Count(Dowa_PHashMap p_hash_map)
+{
+  if (!p_hash_map) return 0;
+  return p_hash_map->current_capacity;
 }
 
 void Dowa_HashMap_Print(Dowa_PHashMap map)
 {
+  if (!map)
+  {
+    printf("HashMap is NULL\n");
+    return;
+  }
+
   printf("\n-----------\n\n");
   for (size_t i = 0; i < map->capacity; ++i)
   {
@@ -347,6 +476,9 @@
 
 int Dowa_HashMap_Cache_Folder(Dowa_PHashMap p_hash_map, const char *folder_path)
 {
+  if (!p_hash_map || !folder_path)
+    return -1;
+
   DIR *dir = opendir(folder_path);
   if (!dir) { perror("opendir"); return -1; }
 
@@ -371,9 +503,9 @@
       FILE *f = fopen(fullpath, "rb");
       if (!f) { perror("fopen"); continue; }
 
-      void *buf = p_hash_map->p_arena ? 
-        Dowa_Arena_Allocate(p_hash_map->p_arena, size) :
-        malloc(size);
+      void *buf = p_hash_map->p_arena ?
+        Dowa_Arena_Allocate(p_hash_map->p_arena, size + 1) :
+        malloc(size + 1);
       if (!buf) { perror("malloc"); fclose(f); closedir(dir); return -1; }
 
 
@@ -386,8 +518,10 @@
       }
       fclose(f);
 
-      Dowa_HashMap_Push_Value_With_Type(p_hash_map, entry->d_name, buf, size, DOWA_HASH_MAP_TYPE_STRING);
-      Dowa_Free(buf);  // Dowa_HashMap_PushValue made its own copy
+      ((char *)buf)[size] = '\0';
+      Dowa_HashMap_Push_Value_With_Type(p_hash_map, entry->d_name, buf, size + 1, DOWA_HASH_MAP_TYPE_STRING);
+      if (!p_hash_map->p_arena)
+        Dowa_Free(buf);  // Dowa_HashMap_PushValue made its own copy
     }
     else if (S_ISDIR(st.st_mode))
     {
--- a/dowa/dowa.h	Tue Dec 16 21:01:45 2025 -0500
+++ b/dowa/dowa.h	Sat Dec 20 13:56:01 2025 -0500
@@ -6,6 +6,7 @@
 #include <stdlib.h> // only for malloc, free, stuff
 #include <assert.h> // mostly for TODO
 #include <dirent.h> // some functions loop through files
+#include <math.h> // I am not re-writing stuff I guess...
 
 #include <sys/stat.h>
 #include <limits.h>
@@ -54,6 +55,12 @@
 extern void  Dowa_Arena_Destroy(Dowa_PArena arena);
 /* Strdup but saves within the arena */
 extern void *Dowa_Arena_Copy(Dowa_PArena p_arena, const void *src, size_t size);
+/* Resets the arena offset to 0, allowing reuse without freeing */
+extern void  Dowa_Arena_Reset(Dowa_PArena p_arena);
+/* Returns the current number of bytes allocated in the arena */
+extern size_t Dowa_Arena_Get_Used(Dowa_PArena p_arena);
+/* Returns the remaining capacity in bytes */
+extern size_t Dowa_Arena_Get_Remaining(Dowa_PArena p_arena);
 
 
 // --- HashMap --- //
@@ -64,7 +71,7 @@
   DOWA_HASH_MAP_TYPE_INT       // Integer value
 } Dowa_HashMap_ValueType;
 
-typedef struct {
+typedef struct Dowa_HashEntry {
   char                  *key;
   void                  *buffer;
   size_t                 capacity;
@@ -97,6 +104,12 @@
 extern int32         Dowa_HashMap_Push_Value_With_Type_NoCopy(Dowa_PHashMap p_hash_map, const char *key, void *value, size_t value_size, Dowa_HashMap_ValueType type);
 /* Removes the entry with the specified key from the hashmap and frees its data if owned. */
 extern void          Dowa_HashMap_Pop_Key(Dowa_PHashMap p_hash_map, const char *key);
+/* Returns TRUE if the key exists in the hashmap, FALSE otherwise. */
+extern boolean       Dowa_HashMap_Has_Key(Dowa_PHashMap p_hash_map, const char *key);
+/* Removes all entries from the hashmap without destroying it. */
+extern void          Dowa_HashMap_Clear(Dowa_PHashMap p_hash_map);
+/* Returns the number of entries currently in the hashmap. */
+extern uint32        Dowa_HashMap_Get_Count(Dowa_PHashMap p_hash_map);
 
 // --- Utility Functions --- //
 /* Prints all entries in the hashmap to stdout for debugging purposes. */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/LRU.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,46 @@
+from typing import Optional
+
+
+class Node:
+    def __init__(self, key, value):
+        self.key = key
+        self.value = value
+        self.next: Optional[Node] = None
+        self.prev: Optional[Node] = None
+
+class LRUCache:
+
+    def __init__(self, capacity: int):
+        self.capacity = capacity
+        self.map = {}
+        self.head = Node(-1, -1)
+        self.tail = Node(-1, -1)
+        self.head.next, self.tail.prev = self.tail, self.head
+
+    def get(self, key: int) -> int:
+        if not key in self.map:
+            return -1
+        node = self.map[key]
+        self._deleteAndAdd(node)
+        return node.value
+        
+    def put(self, key: int, value: int) -> None:
+        newNode = Node(key, value)
+        if key in self.map:
+            self._delete(self.map[key])
+        else:
+            if len(self.map.keys())+1 > self.capacity:
+                del self.map[self.head.next.key]
+                self._delete(self.head.next)
+        self._add(newNode)
+        self.map[key] = newNode
+
+    def _deleteAndAdd(self, node):
+        self._delete(node)
+        self._add(node)
+
+    def _delete(self, node):
+        node.prev.next, node.next.prev = node.next, node.prev
+
+    def _add(self, node):
+        node.prev, node.next, self.tail.prev.next, self.tail.prev = self.tail.prev, self.tail, node, node
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/README.md	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,43 @@
+# Distributed Cache (Focus: Hashing & System Logic)
+
+- Question: Implement a Cache Router
+
+  Level 1 (Easy - Modulo Hashing): Write a function that maps a key (string) to one of N servers.
+
+      Task: Implement getServerIndex(key, serverCount).
+
+      Expected Logic: Simple hash modulo: hash(key) % serverCount.
+
+      Critique: The interviewer will ask, "What happens to the existing keys if we add one server (change N to N+1)?" (Answer: Almost all keys get remapped, causing a massive cache miss storm).
+
+  Level 2 (Medium - Consistent Hashing): Solve the remapping problem. Implement a ConsistentHash class.
+
+      Task: Map servers to points on a circle (0 to 360 degrees or a large integer range). Map keys to the same circle. A key belongs to the first server it encounters moving clockwise.
+
+      Goal: Prove that adding a server only requires remapping the keys that fall between the new server and its predecessor.
+
+  Level 3 (Hard - Virtual Nodes): The servers have uneven capacity, or the data distribution is skewed (some segments of the ring have way more keys).
+
+      Task: Modify the class so that each physical server exists at multiple points on the ring (e.g., "Server A" is at positions 10, 150, and 290).
+
+      Goal: Distribute load more evenly and handle "hot spots" better.
+
+---
+
+Questions:
+
+  - Async
+  - Threads
+  - DB 
+  - Cache
+  - KV
+  - Distribution
+
+
+--- 
+
+FE
+
+ - Fetching URL
+    - Storing it as useMemo and what not.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/alphabet_search.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,56 @@
+# Given an m x n grid of characters board and a string word, return true if word exists in the grid.
+# 
+# The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
+# 
+#  
+# 
+# Example 1:
+# 
+# 
+# Input: board = [
+#   ["A","B","C","E"],
+#   ["S","F","C","S"],
+#   ["A","D","E","E"]
+# ], word = "ABCCED"
+# Output: true
+
+def main(board, word):
+    ROWS = len(board)
+    COLS = len(board[0])
+
+    pos = 0
+
+    directions = [
+        (0, 1),
+        (1, 0),
+        (0, -1),
+        (-1, 0),
+    ]
+    def dfs(row, col, pos):
+        # import pdb; pdb.set_trace()
+        if pos == len(word):
+            return True
+
+        if (row < 0 or row >= ROWS) or (col < 0 or col >= COLS) or board[row][col] == "#":
+            return False
+
+        letter = board[row][col]
+        if letter == word[pos]:
+            pos += 1
+            board[row][col] = "#"
+            for direction in directions:
+                if dfs(row + direction[0], col + direction[1], pos):
+                    return True
+            board[row][col] = letter
+
+    for r in range(ROWS):
+        for c in range(COLS):
+            if dfs(r, c, 0):
+                return True
+    return False
+
+
+board = [["b"],["a"],["b"],["b"],["a"]]
+word = "baa"
+
+print(main(board, word))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/async.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,48 @@
+
+# 1. asynciously down list of urls
+# 2. retries
+
+
+
+import asyncio
+from concurrent.futures import ThreadPoolExecutor
+import random
+from typing import List
+import time
+
+async def interface_download_url(url: str, retry_number = 0):
+    curr = time.time()
+    success = False
+    try:
+        res = await func_download_url(url)
+        success = True
+    except:
+        res = await interface_download_url(url, retry_number + 1)
+        res = None
+    long = curr - time.time()
+    return success, res, url, long
+    
+async def func_download_url(url: str):
+    await asyncio.sleep(random.randint(1, 10))
+
+ALLOWED_BATCH_SIZE = 10
+ALLOWED_RETRY_SIZE = 3
+async def download_multi_urls(urls: List[str], retry_num = 0):
+    if retry_num > ALLOWED_RETRY_SIZE:
+        return
+
+    number_of_batch = len(urls) // ALLOWED_BATCH_SIZE + 1
+
+    errors = []
+    for batch_num in range(number_of_batch):
+        download_urls = urls[batch_num * ALLOWED_BATCH_SIZE:(batch_num + 1) * ALLOWED_BATCH_SIZE]
+        for download_url in download_urls:
+            asyncio.run(asyncio.create_task(interface_download_url(download_url)))
+        results = await asyncio.gather(*tasks)
+        for task in tasks
+        asyncio.run()
+        for result in results:
+            if not result[0]:
+                errors.append(result[2])
+
+    await download_multi_urls(errors, retry_num+1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/binary_search.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,18 @@
+
+x = [1,2,3,4,5,9,20,25,33,99]
+#    |          |
+
+target  = 18
+left = 0
+right = len(x)
+
+while left < right:
+    mid = (left + right)//2
+    if x[mid] == target:
+        break
+    elif x[mid] < target:
+        left = mid + 1
+    else:
+        right = mid
+
+print(x[mid])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/bucket_try_2.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,65 @@
+# Context
+# 
+# Please implement two functions: refillTokenBucket and useTokens. Two classes are already defined: DistributedCache and TokenBucket.
+# 
+# refillTokenBucket(user_id): Refill tokens for the specified user's bucket.
+# 
+# useTokens(user_id, tokens): Check if there are enough tokens in the specified user's token bucket. If so, update the remaining token count and return true; otherwise, return false. You are required to use instances of the existing classes to implement these functions.
+
+import asyncio
+from typing import Dict
+
+
+class DistributedCache:
+
+    def __init__(self):
+        self._hashmap: Dict[str, TokenBucket] = {}
+
+    def add_user(self, user_id: str):
+        self._hashmap[user_id] = TokenBucket()
+
+    def get_token_bucket(self, user_id: str):
+        return self._hashmap[user_id] 
+
+class TokenBucket:
+
+    def __init__(self, initial_token_value: int = 100):
+        self._tokens = initial_token_value
+        self.initial_token_value = initial_token_value
+        self._lock = asyncio.Lock()
+
+    async def consume(self, token: int):
+        async with self._lock:
+            tokens = self.get_token()
+            await asyncio.sleep(0.1)
+            if tokens < token:
+                return False
+            tokens -= token
+            self.set_token(tokens)
+            return True
+
+    async def refill(self):
+        async with self._lock:
+            self._tokens = self.initial_token_value
+
+    def get_token(self):
+        return self._tokens
+
+    def set_token(self, token: int):
+        self._tokens = token
+
+distCache = DistributedCache()
+distCache.add_user("june")
+
+async def refillTokenBucket(user_id: str):
+    await distCache.get_token_bucket(user_id).refill()
+
+async def useTokens(user_id: str, token: int):
+    return await distCache.get_token_bucket(user_id).consume(token)
+
+async def main():
+    await asyncio.gather(useTokens("june", 10), useTokens("june", 10), useTokens("june", 10))
+    print(distCache.get_token_bucket("june").get_token())
+
+asyncio.run(main())
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/choice.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,11 @@
+import random
+
+print(random.choice([
+    'Bucket questions',
+    'Inference Questions',
+    'Bank Account',
+    'Job Schedular',
+    'Alphabet Word Search II',
+    'Least Recently Used (LRU) Cache Implementation',
+    'In-Memory Key-Value Store with Nested Transactions',
+]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/consistent_hash.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,32 @@
+# Write a function taht maps
+# Key string to one of N servers
+from bisect import bisect_left
+from hashlib import sha256
+
+class Server:
+
+    def __init__(self):
+        self.id = str(sha256())
+
+class ConsistentHash:
+    def __init__(self):
+        self.server = []
+        self.hash_key_to_server = {}
+        self.hash_range = 10000
+
+    def get_hash_pos(self, key: str) -> int:
+        x = sha256(bytes(key, "utf-8"))
+        return int.from_bytes(x.digest()[:8]) % self.hash_range
+
+    def add_server(self, server: Server):
+        self.server.append(server)
+        for i in range(len(self.server) + 1):
+            hash_key = self.get_hash_pos(server.id)
+            self.hash_key_to_server[hash_key] = i
+
+    def get_server(self, key):
+        hash_key = self.get_hash_pos(key)
+        return self.server[bisect_left(list(self.hash_key_to_server.keys()), hash_key)]
+
+    def remove_server(self, server):
+        hash_key = self.get_hash_pos(server.id)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/inference.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,146 @@
+import asyncio
+import time
+from typing import List, Dict, Any, Optional
+import uuid
+from dataclasses import dataclass, field
+
+@dataclass
+class InferenceRequest:
+    prompt: str
+    request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
+    max_tokens: int = 128
+    created_at: float = field(default_factory=time.time)
+
+@dataclass
+class TokenOutput:
+    token: str
+    request_id: str
+    token_index: int  # position in this request's generation
+    is_complete: bool = False
+
+class BatchInferenceEngine:
+
+    def __init__(self, batch_size: int = 8, timeout: float = 0.1):
+        """
+        Args:
+            batch_size: Maximum number of requests to process in one inference call
+            timeout: Max time to wait for batch to fill before processing (in seconds)
+        """
+        self.batch_size = batch_size
+        self.timeout = timeout
+        
+        self.queue: List[InferenceRequest] = []
+        self.pending_requests: Dict[str, List[str]] = {}  # request_id -> list of generated tokens
+        self.completion_futures: Dict[str, asyncio.Future] = {}
+        
+        self._lock = asyncio.Lock()
+        self._batch_ready_event = asyncio.Event()
+        
+    async def submit_request(self, prompt: str, max_tokens: int = 128) -> asyncio.Future[str]:
+        """
+        Submit a new inference request and return a Future that resolves to the generated text.
+        """
+        request = InferenceRequest(prompt=prompt, max_tokens=max_tokens)
+        
+        future = asyncio.get_event_loop().create_future()
+        
+        async with self._lock:
+            self.queue.append(request)
+            self.pending_requests[request.request_id] = []
+            self.completion_futures[request.request_id] = future
+            
+            # Trigger batch processing if we've reached batch size
+            if len(self.queue) >= self.batch_size:
+                self._batch_ready_event.set()
+        
+        # Start background worker if not already running
+        asyncio.create_task(self._worker())
+        
+        return future
+    
+    async def _worker(self):
+        """Background task that processes batches when ready"""
+        while True:
+            # Wait until we have requests or timeout triggers
+            try:
+                await asyncio.wait_for(self._batch_ready_event.wait(), timeout=self.timeout)
+            except asyncio.TimeoutError:
+                pass
+            
+            async with self._lock:
+                if not self.queue:
+                    self._batch_ready_event.clear()
+                    continue
+                
+                # Take up to batch_size requests
+                current_batch = self.queue[:self.batch_size]
+                self.queue = self.queue[self.batch_size:]
+                
+                # Reset event if queue is now empty
+                if not self.queue:
+                    self._batch_ready_event.clear()
+            
+            # Simulate batched LLM inference
+            token_outputs: List[TokenOutput] = self._simulate_batched_inference(current_batch)
+            
+            # Distribute tokens back to individual requests
+            async with self._lock:
+                for token_out in token_outputs:
+                    req_id = token_out.request_id
+                    self.pending_requests[req_id].append(token_out.token)
+                    
+                    # Check if this request is complete
+                    if token_out.is_complete:
+                        full_text = ''.join(self.pending_requests[req_id])
+                        future = self.completion_futures.pop(req_id)
+                        future.set_result(full_text)
+                        del self.pending_requests[req_id]
+    
+    def _simulate_batched_inference(self, batch: List[InferenceRequest]) -> List[TokenOutput]:
+        """
+        Simulates a real LLM forward pass on a batch.
+        In real implementation, this would call model.generate() with padded batch.
+        """
+        outputs: List[TokenOutput] = []
+        
+        for req in batch:
+            prompt_len = len(req.prompt.split())  # rough estimate
+            num_tokens_to_generate = min(req.max_tokens, 50 + hash(req.request_id) % 30)
+            
+            # Simulate token-by-token generation
+            token_str = f"{req.prompt}"
+            is_complete = (i == num_tokens_to_generate - 1)
+            
+            outputs.append(TokenOutput(
+                token=token_str,
+                request_id=req.request_id,
+                token_index=i,
+                is_complete=is_complete
+            ))
+            
+            # In streaming: could yield early here in real streaming setup
+        
+        return outputs
+    
+# Example usage
+async def main():
+    engine = BatchInferenceEngine(batch_size=4, timeout=0.5)
+    
+    # Submit several requests
+    futures = [
+        engine.submit_request("Tell me a joke about cats"),
+        engine.submit_request("Explain quantum computing in simple terms"),
+        engine.submit_request("Write a haiku about AI"),
+        engine.submit_request("What is the meaning of life?"),
+    ]
+    
+    print("Requests submitted, waiting for results...")
+    
+    # Wait for all to complete
+    results = await asyncio.gather(*futures)
+    
+    for i, text in enumerate(results):
+        print(f"Request {i}: {text.result()}\n\n\n")
+
+if __name__ == "__main__":
+    asyncio.run(main())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/inference_my_version.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,75 @@
+"""
+Inference Questions
+
+Context
+
+You are tasked with building a simplified inference engine component responsible for handling incoming user requests for a large language model (LLM). To optimize throughput and GPU utilization, the engine must batch multiple requests together, run the inference call once per batch, and then deconstruct the results to return token-level output to the individual users.
+
+Objective
+
+Complete the provided Python class, BatchInferenceEngine by implementing the methods necessary to:
+
+Queue incoming user requests.
+
+Process a batch when the queue reaches a defined batch size.
+Simulate the token-level output from an LLM and correctly associate each generated token with its original request.
+"""
+
+import asyncio
+from dataclasses import dataclass, field
+from time import time
+from typing import Dict, List
+import uuid
+
+@dataclass
+class UserRequest:
+    prompt: str
+    id: str = field(default_factory=lambda: str(uuid.uuid4()))
+    created_at: float = field(default_factory=time)
+
+
+@dataclass
+class TokenOutput:
+    request_id: str
+    token: bytes 
+
+class BatchInferenceEngine:
+
+    def __init__(self, batch_sizes: int = 8):
+        self.queue = []
+        self.request_token_map: Dict[str, str] = {}
+        self.batch_sizes = batch_sizes
+
+        self._lock = asyncio.Lock()
+        self._batch_event = asyncio.Event()
+
+    async def add_to_queue(self, request: UserRequest):
+        async with self._lock:
+            self.queue.append(request)
+
+            if len(self.queue) > self.batch_sizes:
+                self._batch_event.set()
+            
+        task = asyncio.create_task(self._batch())
+        return task
+
+    async def _batch(self):
+        while True:
+            try:
+                await asyncio.wait_for(self._batch_event.wait(), timeout=0.05)
+            except:
+                raise Exception("Timed out")
+
+            async with self._lock:
+                if not self.queue:
+                    self._batch_event.clear()
+                    continue 
+
+                batch = self.queue[:self.batch_sizes]
+                tokens = await self._handle_prompt_to_token(batch)
+
+            return tokens
+
+
+    async def _handle_prompt_to_token(self, batch: List[UserRequest]):
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/job_scheduler.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,43 @@
+# Job Schedular
+# 
+# Implement a concurrent job scheduler that can schedule multiple workers to complete job tasks. The scheduler must handle asynchronous tasks and be able to dynamically increase or decrease the number of workers. It must handle task execution failures, for example, by retrying or logging. Provide examples with at least three different tasks and examples of scheduling strategies.
+
+import asyncio
+from asyncio import Task
+import random
+
+# Rename to match standard library function
+async def job(name: str, delay: int = 1):
+    # Use asyncio.sleep
+    await asyncio.sleep(delay)
+    val = random.randint(1, 10)
+    if val > 5:
+        raise Exception("Something went wrong")
+    else:
+        print(name)
+
+class JobScheduler:
+    def __init__(self):
+        self._jobs: list[Task] = []
+
+    def add_task(self, task: Task):
+        self._jobs.append(task)
+        print(f"Added job: {task.get_name()}") # Added for debugging
+
+    async def run(self):
+        print("Starting concurrent jobs...")
+        await asyncio.gather(*self._jobs, return_exceptions=True)
+        print("All jobs completed.")
+
+async def main():
+    job_scheduler = JobScheduler()
+    # Use asyncio.create_task and provide a name
+    job_scheduler.add_task(asyncio.create_task(job("JUNE", 3), name="JUNE_JOB"))
+    job_scheduler.add_task(asyncio.create_task(job("PARK", 2), name="PARK_JOB"))
+    job_scheduler.add_task(asyncio.create_task(job("HELLO", 1), name="HELLO_JOB"))
+    await job_scheduler.run()
+
+
+if __name__ == "__main__":
+    # Run the main asynchronous entry point
+    asyncio.run(main())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/lru_version2.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,75 @@
+# 
+# Question: Least Recently Used (LRU) Cache Implementation
+# 
+# Design and implement a Least Recently Used (LRU) cache. The cache should be initialized with a positive integer capacity.
+# 
+# Your implementation must support the following operations with $O(1)$ time complexity for both:
+# 
+# get(key): Retrieve the value of the item identified by key. If the key exists, return its value. If it doesn't, return $-1$. This operation also marks the key as the most recently used.
+# 
+# put(key, value): Insert or update an item. If the key already exists, update its value and mark it as the most recently used. If the key does not exist, insert the new item. When the cache reaches its capacity, it must evict the least recently used item before inserting the new one.
+# 
+# You must use a Hash Map and a Doubly Linked List for the implementation.
+
+# Cache , limit 3
+
+from typing import Dict
+
+# tail.prev.next = node, node.left = tails.prev, tails.prev = Node, node.next = tail
+
+# Head <-> Tail
+# Head ->     Node  <-> Tail   , 
+# Head <-> Node <-> Node1 <-> Tail, 
+# node.prev.next = node.next, node.next.prev = node.prev
+
+class Node:
+
+    def __init__(self, key: str, value: str):
+        self.next: Node | None = None
+        self.prev: Node | None = None
+        self.key = key
+        self.value = value
+
+class LRU:
+
+    def __init__(self, capacity: int = 2):
+        self.db: Dict[str, Node] = {}
+        self.capacity = capacity
+
+        self.head = Node("-1", "-1")
+        self.tail = Node("-1", "-1")
+        self.head.next, self.tail.prev = self.tail, self.head
+
+    def get(self, key: str):
+        node = self.db[key]
+        self._remove(node)
+        self._add_to_tail(node)
+        return node.value
+
+    def put(self, key: str, value: str):
+        if len(self.db) < self.capacity or key in self.db:
+            new_node = Node(key, value)
+            self._add_to_tail(new_node)
+            self.db[key] = new_node
+        else:
+            least_recently_used_node = self.head.next
+            self._remove(least_recently_used_node)
+            del self.db[least_recently_used_node.key]
+
+            new_node = Node(key, value)
+            self._add_to_tail(new_node)
+            self.db[key] = new_node
+
+    def _remove(self, node: Node):
+        node.prev.next, node.next.prev = node.next, node.prev
+
+    def _add_to_tail(self, node):
+        self.tail.prev.next, node.prev, self.tail.prev, node.next = node, self.tail.prev, node, self.tail 
+
+lru = LRU()
+lru.put("june", "park")
+lru.put("victor", "sun")
+lru.get("june")
+lru.put("bowen", "sun")
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/main.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,51 @@
+# You are required to implement a production-level code live, using concurrency. Complete within 60 minutes. Suppose you are developing a simple concurrent counter that can correctly update the count in a multi-threaded environment. The initial count value is 0. Ensure it executes in 100 threads, where each thread increments the counter by 1 every second for 10 seconds. Calculate the final count after parallel execution.
+# 
+# Input: None
+# 
+# Output: An integer representing the final count.
+# 
+# Constraints:
+# 
+# The code must use multi-threading or multi-processing techniques.
+# Ensure thread safety.
+
+
+# - 100 threads
+# - each tread update counter by 1 everyseconds by 10 seconds
+from threading import Lock
+from time import sleep
+from concurrent.futures import ThreadPoolExecutor
+
+
+SLEEP_IN_SECONDS = 1
+EXECUTION_TIME_IN_SECONDS = 3
+NUMS_THREADS = 100
+
+class Counter:
+
+    def __init__(self):
+        self._value = 0
+        self.lock = Lock()
+
+    def get(self):
+        return self._value
+
+    def increment(self):
+        with self.lock:
+            value = self.get()
+            sleep(0.01)
+            self._value = value + 1
+
+def worker(counter: Counter):
+    for _ in range(EXECUTION_TIME_IN_SECONDS):
+        counter.increment()
+        sleep(SLEEP_IN_SECONDS)
+
+def main():
+    counter = Counter()
+    with ThreadPoolExecutor(max_workers=NUMS_THREADS) as executor:
+        for _ in range(NUMS_THREADS):
+            executor.submit(worker, counter)
+    print(counter.get())
+
+main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/merge_sort.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,48 @@
+from concurrent.futures import ProcessPoolExecutor
+import random
+from typing import List
+
+def merge_sort(lst: List[int]) -> List[int]:          # no executor parameter!
+    if len(lst) <= 1:
+        return lst
+    middle = len(lst) // 2
+    # These run in separate processes → no shared thread pool problem
+    left = merge_sort(lst[:middle])
+    right = merge_sort(lst[middle:])
+    return merge(left, right)
+
+def merge(left: List[int], right: List[int]) -> List[int]:
+    res = []
+    i = j = 0
+    while i < len(left) and j < len(right):
+        if left[i] <= right[j]:
+            res.append(left[i])
+            i += 1
+        else:
+            res.append(right[j])
+            j += 1
+    res.extend(left[i:])
+    res.extend(right[j:])
+    return res
+
+RANGE = 10_000
+cpu_num = 10
+def main():
+    data = [random.randint(0, 100) for _ in range(RANGE)]
+    sorted_data = []
+    ranges = [i for i in range(RANGE // cpu_num, RANGE, RANGE // 100)]
+    with ProcessPoolExecutor() as executor:
+        for pos_range in ranges:
+            sorted_data.append(executor.submit(merge_sort, data[:pos_range]).result())
+
+    while len(sorted_data) > 1:
+        new_sorted_data = []
+        for i in range(0, len(sorted_data), 2):
+            if 
+            res = merge(sorted_data[i], sorted_data[i+1])
+            new_sorted_data.append(res)
+
+    print(len(sorted_data))
+
+if __name__ == "__main__":      # important on Windows/macOS
+        main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/test.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,36 @@
+import asyncio
+import random
+
+
+class database:
+    def __init__(self):
+        self.values = 0
+        self._lock = asyncio.Lock()
+
+    def get(self):
+        return self.values
+
+    def set(self, values):
+        self.values = values
+
+
+async def add(number, db: database, random_wait: int):
+    async with db._lock:
+        current = db.get()
+        await asyncio.sleep(random.random() * 0.05) 
+        db.set(current + number)
+
+
+async def main():
+    db = database()
+    tasks = []
+    for i in range(100):
+        tasks.append(asyncio.create_task(
+            add(i, db, random.randint(1, 5))
+        ))
+    await asyncio.gather(*tasks)
+    print(db.get())
+    print(sum([i for i in range(100)]))
+
+asyncio.run(main())
+        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/thread.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,32 @@
+# 1. data struture {}
+#    - multiple threads can access it
+#    - multiple threads can edit it if it is editable. (mutex)
+
+
+from hashlib import sha256
+from threading import Lock
+
+class Subscribers:
+    def __init__(self):
+        self.id = sha256()
+
+    def on_event(self, event):
+        pass
+
+class Publisher:
+    def __init__(self):
+        self._subscritions = {}
+        self.lock = Lock()
+
+    def publish_event(self, event):
+        for subscriber_id, subscriber in enumerate(self._subscritions):
+            subscriber.on_event(event)
+
+    def add_subscribers(self, subscriber: Subscribers):
+        with self.lock:
+            self._subscritions[subscriber.id] = subscriber
+
+    def remove_subscribers(self, subscriber: Subscribers):
+        with self.lock:
+            del self._subscritions[subscriber.id]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/transaction.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,57 @@
+# Question: In-Memory Key-Value Store with Nested Transactions
+# 
+# Design an in-memory key-value database that supports the following commands and fully handles nested transactions.
+# Core Operations:
+
+# SET <key> <value>: Sets the value for a key.
+ 
+# GET <key>: Returns the value for a key.
+ 
+# UNSET <key>: Removes the key and its value.
+# 
+# Transaction Operations:
+
+# BEGIN: Starts a new transaction scope. If a transaction is already active, this starts a nested transaction.
+
+# COMMIT: Applies all changes made in the current transaction scope and all its active nested transactions to the parent scope (or to the main database state if no parent exists). After a successful commit, the transaction scope is closed.
+
+# ROLLBACK: Discards all changes made in the current transaction scope and all its active nested transactions, restoring the state to what it was when the BEGIN command was issued for the current scope. After a successful rollback, the transaction scope is closed.
+
+# Implementation Goal:
+# Design the primary data structures and outline the logic for SET, COMMIT, and ROLLBACK to ensure nested transactions operate correctly. Explain how the state of the database is managed across multiple transaction layers.
+
+
+class Database:
+    def __init__(self):
+        self.global_db = {}
+        self.queue = []
+
+    def commit(self):
+        copy_db = self.queue.pop()
+        if self.queue:
+            self.queue[-1].merge(copy_db)
+        else:
+            self.global_db |= copy_db
+
+    def set(self, key, value):
+        if self.queue:
+            curr_db = self.queue[-1]
+            curr_db[key] = value
+        else:
+            self.global_db[key] = value
+
+    def get(self, key):
+        if self.queue:
+            for copy_db in reversed(self.queue):
+                if key in copy_db:
+                    return copy_db[key]
+
+        return self.global_db[key]
+
+    def unset(self, key):
+        if self.queue:
+            curr_db = self.queue[-1]
+            del curr_db[key]
+        else:
+            del self.global_db[key]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grok_interview/tries.py	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,95 @@
+# Question: Alphabet Word Search II
+# 
+# You are given an M x N board of characters and a list of strings called dictionary. Find all unique words in the dictionary that can be found in the board.
+# 
+# A word can be constructed from letters of sequentially adjacent cells, where "adjacent" cells are those horizontally, vertically, or diagonally neighboring. The same letter cell may not be used more than once in a single word.
+# 
+# Implement a function findWords(board, dictionary) that returns a list of all unique words found.
+# 
+# Example:
+# 
+# Board:
+# 
+# [['o', 'a', 'b', 'n'],
+#  ['o', 't', 'a', 'e'],
+#  ['a', 'h', 'k', 'r'],
+#  ['a', 'f', 'l', 'v']]
+# 
+ 
+# Dictionary: ["oath", "pea", "eat", "rain", "hobo", "oats"]
+# 
+# Expected Output: ["oath", "eat", "hobo", "oats"]
+# 
+# Constraints & Requirements:
+ 
+# The search should be optimized for large dictionaries.
+ 
+# Clearly explain your chosen data structure and algorithm for prefix matching and path finding.
+
+from collections import deque
+from os import wait
+from typing import List
+
+
+board = [
+ ['o', 'a', 'b', 'n'],
+ ['o', 't', 'a', 'e'],
+ ['a', 'h', 'k', 'r'],
+ ['a', 'f', 'l', 'v']
+]
+dictionary = ["oath", "pea", "eat", "rain", "hobo", "oats"]
+directions = [
+    (0, 1),
+    (0, -1),
+    (1, 0),
+    (-1, 0),
+    (1, 1),
+    (-1, -1),
+    (1, -1),
+    (-1, 1),
+]
+
+def find_words(board: List[List[str]], dictionary: List[str]):
+    tree = {}
+    for row in range(len(board)):
+        for col in range(len(board)):
+            dfs(row, col, tree)
+
+    ans = []
+    for word in dictionary:
+        curr = tree
+        possible = True
+        for letter in word:
+            if letter not in curr:
+                possible = False
+                break
+            curr = curr[letter]
+        if possible:
+            ans.append(word)
+
+    return ans
+
+
+def dfs(row, col, tree):
+    if not (row >= 0 and row < len(board)\
+            and col >= 0 and col < len(board[0])):
+        return {}
+
+    if board[row][col] == "#":
+        return {}
+
+    letter = board[row][col]
+    children = {}
+    board[row][col] = "#"
+
+    for direction in directions:
+        new_row, new_col = row + direction[0], col + direction[1]
+        child_node = dfs(new_row, new_col, tree[letter])
+        if child_node:
+            children.update(child_node)
+
+    board[row][col] = letter
+
+    return { letter: children }
+
+print(find_words(board, dictionary))
--- a/gui_ze/gui_ze.bzl	Tue Dec 16 21:01:45 2025 -0500
+++ b/gui_ze/gui_ze.bzl	Sat Dec 20 13:56:01 2025 -0500
@@ -10,7 +10,6 @@
   implementation = _foo_impl,
 )
 
-
 def _bundle_impl(ctx):
   """
   bundle binary target into a folder which can later be used to make a post to github easily.
@@ -183,3 +182,169 @@
     "dest": attr.string(),
   },
 )
+
+
+def _macos_app_impl(ctx):
+    """Implementation for creating a macOS .app bundle from a binary."""
+    binary = ctx.file.binary
+    app_name = ctx.attr.app_name
+    bundle_id = ctx.attr.bundle_id
+
+    # Declare the .app directory as output
+    app_dir = ctx.actions.declare_directory(app_name + ".app")
+
+    ctx.actions.run_shell(
+        inputs = [binary],
+        outputs = [app_dir],
+        command = """
+        set -e
+
+        APPDIR="{app_dir}"
+
+        rm -rf "$APPDIR"
+        mkdir -p "$APPDIR/Contents/MacOS"
+        mkdir -p "$APPDIR/Contents/Resources"
+
+        cp {binary} "$APPDIR/Contents/MacOS/{app_name}"
+        chmod +x "$APPDIR/Contents/MacOS/{app_name}"
+
+        cat > "$APPDIR/Contents/Info.plist" <<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleExecutable</key>
+  <string>{app_name}</string>
+  <key>CFBundleIdentifier</key>
+  <string>{bundle_id}</string>
+  <key>CFBundleName</key>
+  <string>{app_name}</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+</dict>
+</plist>
+EOF
+        """.format(
+            app_dir = app_dir.path,
+            binary = binary.path,
+            app_name = app_name,
+            bundle_id = bundle_id,
+        ),
+        progress_message = "Creating macOS app bundle for {}".format(app_name),
+    )
+
+    return [DefaultInfo(files = depset([app_dir]))]
+
+_macos_app = rule(
+    implementation = _macos_app_impl,
+    attrs = {
+        "binary": attr.label(
+            allow_single_file = True,
+            mandatory = True,
+            doc = "The binary to bundle into a .app",
+        ),
+        "app_name": attr.string(
+            mandatory = True,
+            doc = "Name of the application",
+        ),
+        "bundle_id": attr.string(
+            mandatory = True,
+            doc = "Bundle identifier (e.g., com.example.app)",
+        ),
+    },
+)
+
+def _macos_signed_app_impl(ctx):
+    """Implementation for signing a macOS .app bundle."""
+    app_dir = ctx.file.app
+    app_name = ctx.attr.app_name
+
+    # Declare the signed .app directory as output
+    signed_app_dir = ctx.actions.declare_directory(app_name + "_signed.app")
+
+    ctx.actions.run_shell(
+        inputs = [app_dir],
+        outputs = [signed_app_dir],
+        command = """
+        set -e
+
+        APPDIR="{app_dir}"
+        SIGNEDDIR="{signed_dir}"
+
+        rm -rf "$SIGNEDDIR"
+        # Use -L to dereference symlinks, as codesign doesn't work with symlinks
+        cp -RL "$APPDIR" "$SIGNEDDIR"
+
+        codesign --deep --force --sign - "$SIGNEDDIR"
+        """.format(
+            app_dir = app_dir.path,
+            signed_dir = signed_app_dir.path,
+        ),
+        progress_message = "Signing macOS app bundle {}".format(app_name),
+    )
+
+    return [DefaultInfo(files = depset([signed_app_dir]))]
+
+_macos_signed_app = rule(
+    implementation = _macos_signed_app_impl,
+    attrs = {
+        "app": attr.label(
+            allow_single_file = True,
+            mandatory = True,
+            doc = "The .app directory to sign",
+        ),
+        "app_name": attr.string(
+            mandatory = True,
+            doc = "Name of the application",
+        ),
+    },
+)
+
+def macos_app_and_dmg(name, binary, bundle_id = "com.example.app"):
+    """
+    Creates a macOS .app bundle, signs it, and packages it into a DMG.
+
+    Args:
+        name: Base name for the generated targets
+        binary: Label of the binary target to bundle
+        bundle_id: Bundle identifier for the app (default: com.example.app)
+
+    Generates:
+        {name}_app: The .app bundle
+        {name}_signed_app: The signed .app bundle
+        {name}_dmg: The final DMG file
+    """
+    # 1. Build the .app bundle
+    _macos_app(
+        name = name + "_app",
+        binary = binary,
+        app_name = name,
+        bundle_id = bundle_id,
+    )
+
+    # 2. Sign the .app
+    _macos_signed_app(
+        name = name + "_signed_app",
+        app = ":" + name + "_app",
+        app_name = name,
+    )
+
+    # 3. Create the DMG
+    native.genrule(
+        name = name + "_dmg",
+        srcs = [":" + name + "_signed_app"],
+        outs = [name + ".dmg"],
+        cmd = """
+        set -e
+
+        SIGNEDDIR="$(location :{name}_signed_app)"
+
+        hdiutil create \
+            -volname {name} \
+            -srcfolder "$$SIGNEDDIR" \
+            -ov -format UDZO "$@"
+        """.format(name = name),
+        local = 1,  # Disable sandboxing for hdiutil
+        tags = ["no-sandbox"],
+    )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/markdown_converter/main.js	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,41 @@
+// #
+// ##
+// []()
+// _ _
+// * *
+// 1.
+// -
+// \s\s
+//
+
+function Lexer(character) {
+}
+
+/**
+ * @params
+ *   value: string
+ */
+function mardownConverter(value) {
+  let i = 0;
+  let character;
+  const domElements = [];
+  while (i < value.length) {
+    character = value[i]
+    switch(character) {
+      case "#": {
+        if 
+        const dom = document.createElement("h1");
+        domElements.append(dom)
+      }
+      case "_": {
+      }
+      case "_" {
+      }
+      case "*" {
+      }
+      default {
+        return character;
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/postdog/BUILD	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,35 @@
+load("//third_party/raylib:raylib.bzl", "raylib_binary")
+load("//gui_ze:gui_ze.bzl", "macos_app_and_dmg")
+
+raylib_binary(
+  name = "postdog",
+  srcs = ["main.c"],
+  deps = [
+    "//third_party/raylib:raylib",
+  ],
+  linkopts_macos = [
+    "-framework CoreVideo",
+    "-framework IOKit",
+    "-framework Cocoa",
+    "-framework GLUT",
+    "-framework OpenGL",
+    "-lcurl",
+  ],
+  linkopts_linux = [
+    "-lGL",
+    "-lm",
+    "-lpthread",
+    "-ldl",
+    "-lrt",
+    "-lX11",
+    "-lcurl",
+  ],
+  static = True
+)
+
+macos_app_and_dmg(
+  name = "postdog_bundle",
+  binary = ":postdog",
+  bundle_id = "com.june.postdog"
+)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/postdog/main.c	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,881 @@
+/**
+ * Entirely written by Claude AI.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+
+// third party
+#include <curl/curl.h>
+#include "third_party/raylib/include/raylib.h"
+#define RAYGUI_IMPLEMENTATION
+#include "third_party/raylib/include/raygui.h"
+
+#define SCREEN_WIDTH 1280
+#define SCREEN_HEIGHT 780
+#define TEXT_SIZE 10
+#define LINE_HEIGHT 15
+#define GENERIC_PADDING 15
+
+#define SIDEBAR_WIDTH 200
+#define SIDEBAR_PADDING_GENERAL 5
+#define SIDEBAR_AREA_PADDING_X 10
+#define SIDEBAR_AREA_PADDING_Y 10
+#define SIDEBAR_REFERSH_BUTTON_WIDTH 90
+#define SIDEBAR_REFERSH_BUTTON_HEIGHT 20
+#define SIDEBAR_HISTORY_ITEM_HEIGHT 45
+
+#define URL_INPUT_HEIGHT 40
+
+#define TAB_LEN 3
+#define METHOD_BUTTON_WIDTH 100
+#define METHOD_BUTTON_HEIGHT 40
+#define SEND_BUTTON_WIDTH 100
+#define SEND_BUTTON_HEIGHT 40
+
+#define JSON_INPUT_BUFFER_LEN 8192
+#define HEADER_INPUT_BUFFER_LEN 4096
+#define PARAM_INPUT_BUFFER_LEN 4096
+#define MAX_HISTORY_ITEMS 100
+
+// Structure to hold response data
+typedef struct {
+  char *data;
+  size_t size;
+} ResponseBuffer;
+
+// Structure to hold history item
+typedef struct {
+  char filename[256];
+  char displayName[128];
+  char method[16];
+  time_t timestamp;
+} HistoryItem;
+
+typedef enum {
+  ActiveTab_JSON = 0,
+  ActiveTab_Headers, 
+  ActiveTab_Params,
+} ActiveTab;
+
+// Callback function for curl to write response data
+static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+  size_t real_size = size * nmemb;
+  ResponseBuffer *buf = (ResponseBuffer *)userp;
+
+  char *ptr = realloc(buf->data, buf->size + real_size + 1);
+  if (ptr == NULL)
+  {
+    printf("Not enough memory for response\n");
+    return 0;
+  }
+
+  buf->data = ptr;
+  memcpy(&(buf->data[buf->size]), contents, real_size);
+  buf->size += real_size;
+  buf->data[buf->size] = 0;
+
+  return real_size;
+}
+
+// Function to make HTTP request using curl
+int PostDog_Make_HttpRequest(const char *url, const char *method, const char *headers,
+                    const char *body, char *response, size_t responseSize)
+{
+  CURL *curl;
+  CURLcode res;
+  ResponseBuffer buffer = { .data = malloc(1), .size = 0 };
+
+  if (buffer.data == NULL)
+  {
+    snprintf(response, responseSize, "Error: Failed to allocate memory");
+    return -1;
+  }
+  buffer.data[0] = '\0';
+
+  curl_global_init(CURL_GLOBAL_DEFAULT);
+  curl = curl_easy_init();
+
+  if (curl)
+  {
+    struct curl_slist *headerList = NULL;
+
+    // Set URL
+    curl_easy_setopt(curl, CURLOPT_URL, url);
+
+    // Set HTTP method
+    if (strcmp(method, "POST") == 0)
+    {
+      curl_easy_setopt(curl, CURLOPT_POST, 1L);
+      if (body && strlen(body) > 0) {
+        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
+      }
+    } 
+    else if (strcmp(method, "PUT") == 0)
+    {
+      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
+      if (body && strlen(body) > 0) {
+        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
+      }
+    }
+    else if (strcmp(method, "DELETE") == 0)
+    {
+      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
+    }
+    // Default is GET
+
+    // Parse and add headers
+    if (headers && strlen(headers) > 0)
+    {
+      char *headersCopy = strdup(headers);
+      char *line = strtok(headersCopy, "\n");
+      while (line != NULL) {
+        // Trim whitespace
+        while (*line == ' ' || *line == '\t') line++;
+        if (strlen(line) > 0) {
+          headerList = curl_slist_append(headerList, line);
+        }
+        line = strtok(NULL, "\n");
+      }
+      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
+      free(headersCopy);
+    }
+
+    // Set write callback
+    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Postdog_Curl_Callback);
+    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);
+
+    // Follow redirects
+    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+
+    // Set timeout
+    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
+
+    // Perform request
+    res = curl_easy_perform(curl);
+
+    if (res != CURLE_OK) {
+      snprintf(response, responseSize, "Error: %s\n", curl_easy_strerror(res));
+    } else {
+      long response_code;
+      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+
+      snprintf(response, responseSize, "HTTP Status: %ld\n\n%s",
+               response_code, buffer.data ? buffer.data : "");
+    }
+
+    // Cleanup
+    if (headerList) curl_slist_free_all(headerList);
+    curl_easy_cleanup(curl);
+  } else {
+    snprintf(response, responseSize, "Error: Failed to initialize curl");
+  }
+
+  free(buffer.data);
+  curl_global_cleanup();
+
+  return 0;
+}
+
+void PostDog_HistoryDirectory_Exists()
+{
+  struct stat st = {0};
+  if (stat("history", &st) == -1)
+  {
+    mkdir("history", 0700);
+  }
+}
+
+int PostDog_HistoryDistory_ItemsLoad(HistoryItem *items, int maxItems)
+{
+  DIR *dir = opendir("history");
+  if (!dir) return 0;
+
+  struct dirent *entry;
+  int count = 0;
+
+  while ((entry = readdir(dir)) != NULL && count < maxItems)
+  {
+    if (entry->d_name[0] == '.') continue;
+    if (strstr(entry->d_name, ".txt") == NULL) continue;
+
+    // Parse filename: YYYYMMDD_HHMMSS_METHOD.txt
+    strncpy(items[count].filename, entry->d_name, sizeof(items[count].filename) - 1);
+
+    // Extract method from filename
+    char *methodStart = strrchr(entry->d_name, '_');
+    if (methodStart)
+    {
+      methodStart++; // Skip underscore
+      char *dotPos = strchr(methodStart, '.');
+      if (dotPos) {
+        int len = dotPos - methodStart;
+        if (len < 16) {
+          strncpy(items[count].method, methodStart, len);
+          items[count].method[len] = '\0';
+        }
+      }
+    }
+
+    // Create display name: METHOD - YYYYMMDD HHMMSS
+    char dateTime[32] = "";
+    if (strlen(entry->d_name) >= 15)
+    {
+      snprintf(dateTime, sizeof(dateTime), "%.8s %.6s",
+               entry->d_name, entry->d_name + 9);
+    }
+    snprintf(items[count].displayName, sizeof(items[count].displayName),
+             "%s - %s", items[count].method, dateTime);
+    count++;
+  }
+
+  closedir(dir);
+  return count;
+}
+
+int PostDog_HistoryDirectory_LoadRequest(const char *filename, char *url, char *method, char *headers, char *body)
+{
+  char filepath[512];
+  snprintf(filepath, sizeof(filepath), "history/%s", filename);
+
+  FILE *f = fopen(filepath, "r");
+  if (!f) return -1;
+
+  char line[2048];
+  int section = 0; // 0=url, 1=method, 2=headers, 3=body
+
+  url[0] = '\0';
+  method[0] = '\0';
+  headers[0] = '\0';
+  body[0] = '\0';
+
+  while (fgets(line, sizeof(line), f))
+  {
+    // Remove newline
+    line[strcspn(line, "\n")] = 0;
+
+    if (section == 0)
+    {
+      // First line is URL
+      strncpy(url, line, 1024 - 1);
+      section = 1;
+    } 
+    else if (strncmp(line, "Method: ", 8) == 0)
+    {
+      strncpy(method, line + 8, 15);
+      section = 2;
+    }
+    else if (strncmp(line, "Headers:", 8) == 0)
+    {
+      section = 2;
+    }
+    else if (strcmp(line, "---") == 0)
+    {
+      section = 3;
+    }
+    else if (strncmp(line, "Body:", 5) == 0)
+    {
+      section = 3;
+    }
+    else if (section == 2 && strcmp(line, "None") != 0)
+    {
+      // Add header line
+      if (strlen(headers) > 0) strcat(headers, "\n");
+      strncat(headers, line, HEADER_INPUT_BUFFER_LEN - strlen(headers) - 1);
+    }
+    else if (section == 3 && strcmp(line, "None") != 0)
+    {
+      // Add body line
+      if (strlen(body) > 0) strcat(body, "\n");
+      strncat(body, line, JSON_INPUT_BUFFER_LEN - strlen(body) - 1);
+    }
+  }
+
+  fclose(f);
+  return 0;
+}
+
+void updateUrlWithParams(char *url, size_t urlSize, const char *baseUrl, const char *params)
+{
+  // Find if there's already a ? in the URL
+  char *questionMark = strchr(baseUrl, '?');
+
+  if (questionMark != NULL) {
+    // URL already has params, just copy the base
+    strncpy(url, baseUrl, urlSize - 1);
+  } else {
+    // No params yet, add them
+    snprintf(url, urlSize, "%s", baseUrl);
+  }
+
+  // Parse and append params
+  if (params && strlen(params) > 0) {
+    char *paramsCopy = strdup(params);
+    char *line = strtok(paramsCopy, "\n");
+    bool firstParam = (questionMark == NULL);
+
+    while (line != NULL) {
+      // Trim whitespace
+      while (*line == ' ' || *line == '\t') line++;
+
+      if (strlen(line) > 0 && strchr(line, '=')) {
+        size_t currentLen = strlen(url);
+        if (currentLen + 2 < urlSize) {
+          strcat(url, firstParam ? "?" : "&");
+          firstParam = false;
+          strncat(url, line, urlSize - strlen(url) - 1);
+        }
+      }
+      line = strtok(NULL, "\n");
+    }
+    free(paramsCopy);
+  }
+}
+
+// Save request to history file
+void saveRequestToHistory(const char *url, const char *method, const char *headers, const char *body)
+{
+  PostDog_HistoryDirectory_Exists();
+
+  // Generate filename with timestamp
+  time_t now = time(NULL);
+  struct tm *t = localtime(&now);
+  char filename[256];
+  snprintf(filename, sizeof(filename), "history/%04d%02d%02d_%02d%02d%02d_%s.txt",
+           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+           t->tm_hour, t->tm_min, t->tm_sec, method);
+
+  FILE *f = fopen(filename, "w");
+  if (f == NULL) {
+    printf("Failed to create history file\n");
+    return;
+  }
+
+  // Write URL
+  fprintf(f, "%s\n", url);
+
+  // Write method
+  fprintf(f, "Method: %s\n", method);
+
+  // Write headers
+  fprintf(f, "Headers:\n");
+  if (headers && strlen(headers) > 0) {
+    fprintf(f, "%s\n", headers);
+  } else {
+    fprintf(f, "None\n");
+  }
+
+  fprintf(f, "---\n");
+
+  // Write body
+  fprintf(f, "Body:\n");
+  if (body && strlen(body) > 0) {
+    fprintf(f, "%s\n", body);
+  } else {
+    fprintf(f, "None\n");
+  }
+
+  fclose(f);
+  printf("Request saved to %s\n", filename);
+}
+
+void PostDog_Render_TextWithScroll(Rectangle textArea, Vector2 scroll, char * input) {
+  BeginScissorMode(textArea.x, textArea.y, textArea.width, textArea.height);
+
+  int yPos = textArea.y + 5 - (int)scroll.y;
+  char *textCopy = strdup(input);
+  char *line = strtok(textCopy, "\n");
+
+  while (line != NULL && yPos < textArea.y + textArea.height)
+  {
+    if (yPos + LINE_HEIGHT > textArea.y) {
+      DrawText(line, textArea.x + 5, yPos, TEXT_SIZE, DARKGRAY);
+    }
+    yPos += LINE_HEIGHT;
+    line = strtok(NULL, "\n");
+  }
+  free(textCopy);
+
+  EndScissorMode();
+}
+
+int main()
+{
+  InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "PostDog - HTTP Client");
+  SetWindowState(FLAG_WINDOW_RESIZABLE);
+  SetTargetFPS(60);
+
+  PostDog_HistoryDirectory_Exists();
+
+  // UI State
+  char urlInput[1024] = "https://httpbin.org/get";
+  bool urlEditMode = false;
+
+  char jsonInput[JSON_INPUT_BUFFER_LEN] = "{\"key\":\"value\"}";
+  bool jsonEditMode = false;
+
+  char headersInput[HEADER_INPUT_BUFFER_LEN] = "Content-Type: application/json";
+  bool headersEditMode = false;
+
+  char responseText[16384] = "Response will appear here...\n\nTry the default URL or enter your own!";
+
+  char paramsInput[PARAM_INPUT_BUFFER_LEN] = "key1=value1\nkey2=value2";
+  bool paramsEditMode = false;
+
+  ActiveTab activeTab = ActiveTab_JSON; // 0 = JSON, 1 = Headers, 2 = Params
+
+  // HTTP method selection
+  int methodActive = 0;
+  bool methodDropdown = false;
+  const char *methods[] = { "GET", "POST", "PUT", "DELETE" };
+
+  // Scroll support
+  Vector2 jsonScroll = { 0, 0 };
+  Vector2 headersScroll = { 0, 0 };
+  Vector2 paramsScroll = { 0, 0 };
+  Vector2 responseScroll = { 0, 0 };
+  Vector2 historyScroll = { 0, 0 };
+
+  // History
+  HistoryItem historyItems[MAX_HISTORY_ITEMS];
+  int historyCount = 0;
+  int selectedHistoryIndex = -1;
+
+  // Load initial history
+  historyCount = PostDog_HistoryDistory_ItemsLoad(historyItems, MAX_HISTORY_ITEMS);
+
+  while (!WindowShouldClose())
+  {
+    // Get current window dimensions for responsive layout
+    int screenWidth = GetScreenWidth();
+    int screenHeight = GetScreenHeight();
+
+    // Layout calculations
+    Rectangle sidebar = { 0, 10, SIDEBAR_WIDTH, screenHeight };
+
+    float mainX = SIDEBAR_WIDTH + GENERIC_PADDING;
+    float mainWidth = screenWidth - SIDEBAR_WIDTH - GENERIC_PADDING * 2;
+
+    // URL input box - leave space for SEND button on the right
+    Rectangle urlBox = {
+      mainX,
+      GENERIC_PADDING,
+      mainWidth - SEND_BUTTON_WIDTH - GENERIC_PADDING,
+      URL_INPUT_HEIGHT
+    };
+
+    // SEND button positioned beside URL input
+    Rectangle sendButton = {
+      urlBox.x + urlBox.width + GENERIC_PADDING,
+      GENERIC_PADDING,
+      SEND_BUTTON_WIDTH,
+      SEND_BUTTON_HEIGHT
+    };
+
+    // Method dropdown below URL
+    Rectangle methodButton = {
+      mainX,
+      urlBox.y + urlBox.height + GENERIC_PADDING,
+      METHOD_BUTTON_WIDTH,
+      METHOD_BUTTON_HEIGHT
+    };
+
+    float tabHeight = 30;
+    float contentY = methodButton.y + methodButton.height + GENERIC_PADDING + tabHeight;
+    float contentHeight = screenHeight - contentY - GENERIC_PADDING;
+
+    float panelWidth = (mainWidth - GENERIC_PADDING) / 2;
+
+    Rectangle tabBar = {
+      mainX,
+      methodButton.y + methodButton.height + GENERIC_PADDING,
+      panelWidth,
+      tabHeight
+    };
+
+    Rectangle requestPanel = {
+      mainX,
+      contentY,
+      panelWidth,
+      contentHeight
+    };
+
+    Rectangle responsePanel = {
+      mainX + panelWidth + GENERIC_PADDING,
+      contentY,
+      panelWidth,
+      contentHeight
+    };
+
+    BeginDrawing();
+      ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));
+
+      // --- Sidebar Component---
+      DrawRectangleRec(sidebar, Fade(GRAY, 0.1f));
+      GuiGroupBox(sidebar, "HISTORY");
+
+      Rectangle refreshBtn = { 
+        sidebar.x + SIDEBAR_PADDING_GENERAL, 
+        sidebar.y + SIDEBAR_PADDING_GENERAL,
+        SIDEBAR_REFERSH_BUTTON_WIDTH,
+        SIDEBAR_REFERSH_BUTTON_HEIGHT
+      };
+      if (GuiButton(refreshBtn, "Refresh"))
+      {
+        historyCount = PostDog_HistoryDistory_ItemsLoad(historyItems, MAX_HISTORY_ITEMS);
+      }
+
+      Rectangle historyArea = { 
+        sidebar.x + SIDEBAR_PADDING_GENERAL,
+        sidebar.y + SIDEBAR_AREA_PADDING_Y, 
+        sidebar.width - SIDEBAR_AREA_PADDING_X, 
+        sidebar.height - SIDEBAR_AREA_PADDING_Y
+      };
+      if (CheckCollisionPointRec(GetMousePosition(), historyArea))
+      {
+        float wheel = GetMouseWheelMove();
+        historyScroll.y += wheel * 20;
+        if (historyScroll.y < 0) historyScroll.y = 0;
+      }
+
+      BeginScissorMode(historyArea.x, historyArea.y, historyArea.width, historyArea.height);
+
+      if (historyCount == 0)
+      {
+        DrawText("No requests yet", historyArea.x + 5, historyArea.y + 25, 10, DARKGRAY);
+      } 
+      else
+      {
+        int item_y_position = historyArea.y + SIDEBAR_AREA_PADDING_Y + 5 - (int)historyScroll.y;
+        for (
+            int current_history_item_number = 0;
+            current_history_item_number < historyCount;
+            current_history_item_number++
+        )
+        {
+          if (item_y_position > historyArea.y - SIDEBAR_HISTORY_ITEM_HEIGHT && item_y_position < historyArea.y + historyArea.height)
+          {
+            Rectangle itemRect = { historyArea.x, item_y_position, historyArea.width, SIDEBAR_HISTORY_ITEM_HEIGHT - 2 };
+
+            // Draw button for history item
+            Color bgColor = (selectedHistoryIndex == current_history_item_number) ? Fade(BLUE, 0.3f) : Fade(LIGHTGRAY, 0.5f);
+            if (CheckCollisionPointRec(GetMousePosition(), itemRect) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+              // TODO: This is cringe as fuck probably should just have a strucut that we assign and then zero out lol
+              char tempUrl[1024], tempMethod[16], tempHeaders[HEADER_INPUT_BUFFER_LEN], tempBody[JSON_INPUT_BUFFER_LEN];
+
+              if (PostDog_HistoryDirectory_LoadRequest(
+                    historyItems[current_history_item_number].filename, tempUrl, tempMethod, tempHeaders, tempBody) == 0)
+              {
+                strncpy(urlInput, tempUrl, sizeof(urlInput) - 1);
+                strncpy(headersInput, tempHeaders, sizeof(headersInput) - 1);
+                strncpy(jsonInput, tempBody, sizeof(jsonInput) - 1);
+
+                // Set method
+                for (int m = 0; m < 4; m++)
+                {
+                  if (strcmp(methods[m], tempMethod) == 0)
+                  {
+                    methodActive = m;
+                    break;
+                  }
+                }
+
+                selectedHistoryIndex = current_history_item_number;
+                strcpy(responseText, "Request loaded from history. Click SEND to execute.");
+              }
+            }
+
+            DrawRectangleRec(itemRect, bgColor);
+            DrawRectangleLinesEx(itemRect, 1, GRAY);
+
+            // Draw method badge
+            DrawText(historyItems[current_history_item_number].method, itemRect.x + 5, item_y_position + 5, 10, BLACK);
+
+            // Draw timestamp (date only)
+            char dateStr[16] = "";
+            if (strlen(historyItems[current_history_item_number].filename) >= 8)
+            {
+              snprintf(dateStr, sizeof(dateStr), "%.4s-%.2s-%.2s",
+                       historyItems[current_history_item_number].filename, historyItems[current_history_item_number].filename + 4, historyItems[current_history_item_number].filename + 6);
+            }
+            DrawText(dateStr, itemRect.x + 5, item_y_position + 18, 8, DARKGRAY);
+
+            // Draw time
+            char timeStr[16] = "";
+            if (strlen(historyItems[current_history_item_number].filename) >= 15) {
+              snprintf(timeStr, sizeof(timeStr), "%.2s:%.2s:%.2s",
+                       historyItems[current_history_item_number].filename + 9, historyItems[current_history_item_number].filename + 11, historyItems[current_history_item_number].filename + 13);
+            }
+            DrawText(timeStr, itemRect.x + 5, item_y_position + 28, 8, DARKGRAY);
+          }
+
+          item_y_position += SIDEBAR_HISTORY_ITEM_HEIGHT;
+        }
+      }
+
+      EndScissorMode();
+
+      // --- URL Input Component ---
+      GuiLabel((Rectangle){ mainX, GENERIC_PADDING - 15, 100, 20 }, "URL:");
+      if (GuiTextBox(urlBox, urlInput, 1024, urlEditMode))
+      {
+        urlEditMode = !urlEditMode;
+      }
+      if (urlEditMode && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C))
+      {
+        SetClipboardText(urlInput);
+      }
+
+      // Send button (beside URL)
+      if (GuiButton(sendButton, "SEND") || (urlEditMode && IsKeyDown(KEY_ENTER)))
+      {
+        strcpy(responseText, "Sending request...\n");
+
+        // Make the actual HTTP request
+        char tempResponse[16384];
+        const char *selectedMethod = methods[methodActive];
+
+        // Use JSON body for POST/PUT, otherwise use empty body
+        const char *requestBody = (methodActive == 1 || methodActive == 2) ? jsonInput : NULL;
+        const char *requestHeaders = headersInput;
+
+        // Save request to history
+        saveRequestToHistory(urlInput, selectedMethod, requestHeaders, requestBody);
+
+        // Reload history to show the new request
+        historyCount = PostDog_HistoryDistory_ItemsLoad(historyItems, MAX_HISTORY_ITEMS);
+
+        // Making the requests.
+        PostDog_Make_HttpRequest(urlInput, selectedMethod, requestHeaders,
+                       requestBody, tempResponse, sizeof(tempResponse));
+        strncpy(responseText, tempResponse, sizeof(responseText) - 1);
+        responseText[sizeof(responseText) - 1] = '\0';
+      }
+
+      // Tab toggle (3 tabs now)
+      float tabWidth = tabBar.width / 3;
+      Rectangle jsonTab = { tabBar.x, tabBar.y - 10, tabWidth, tabBar.height };
+      Rectangle headersTab = { tabBar.x + tabWidth, tabBar.y - 10, tabWidth, tabBar.height };
+      Rectangle paramsTab = { tabBar.x + tabWidth * 2, tabBar.y - 10, tabWidth, tabBar.height };
+
+      if (GuiButton(jsonTab, activeTab ==  ActiveTab_JSON ? "#191#Body" : "Body"))
+      {
+        activeTab = ActiveTab_JSON;
+      }
+
+      if (GuiButton(headersTab, activeTab == ActiveTab_Headers ? "#191#Headers" : "Headers"))
+      {
+        activeTab = ActiveTab_Headers;
+      }
+
+      if (GuiButton(paramsTab, activeTab == ActiveTab_Params ? "#191#Params" : "Params"))
+      {
+        activeTab = ActiveTab_Params;
+      }
+
+      const char *panelTitle;
+      switch(activeTab) {
+        case  ActiveTab_JSON:
+        {
+          panelTitle = "Request Body (JSON)";
+          break;
+        }
+        case  ActiveTab_Headers:
+        {
+          panelTitle = "Request Headers";
+          break;
+        } 
+        case  ActiveTab_Params:
+        {
+          panelTitle = "Query Parameters";
+          break;
+        }
+      }
+
+      // Panel title
+      GuiGroupBox(requestPanel, panelTitle);
+      Rectangle textArea = {
+        requestPanel.x + 10,
+        requestPanel.y + 30,
+        requestPanel.width - 20,
+        requestPanel.height - 40
+      };
+
+      // Handle click outside to disable edit mode
+      if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+      {
+        if (!CheckCollisionPointRec(GetMousePosition(), textArea))
+        {
+          jsonEditMode = false;
+          headersEditMode = false;
+          paramsEditMode = false;
+        }
+      }
+
+      // Draw border for text area
+      DrawRectangleLinesEx(textArea, 1, GRAY);
+
+      // Manual scroll handling with mouse wheel
+      if (CheckCollisionPointRec(GetMousePosition(), textArea))
+      {
+        float wheel = GetMouseWheelMove();
+        switch(activeTab)
+        {
+          case  ActiveTab_JSON:
+          {
+            jsonScroll.y += wheel * 20;
+            if (jsonScroll.y < 0) jsonScroll.y = 0;
+          }
+          case  ActiveTab_Headers:
+          {
+            headersScroll.y += wheel * 20;
+            if (headersScroll.y < 0) headersScroll.y = 0;
+          } 
+          case  ActiveTab_Params:
+          {
+            paramsScroll.y += wheel * 20;
+            if (paramsScroll.y < 0) paramsScroll.y = 0;
+          }
+        }
+      }
+
+      char *copyFromInput;
+      bool *currentMode; 
+      switch(activeTab)
+      {
+        case  ActiveTab_JSON:
+        {
+          PostDog_Render_TextWithScroll(textArea, jsonScroll, jsonInput);
+          copyFromInput = jsonInput;
+          currentMode = &jsonEditMode;
+          break;
+        }
+        case  ActiveTab_Headers:
+        {
+          PostDog_Render_TextWithScroll(textArea, headersScroll, headersInput);
+          copyFromInput = headersInput;
+          currentMode = &headersEditMode;
+          break;
+        } 
+        case  ActiveTab_Params:
+        {
+          PostDog_Render_TextWithScroll(textArea, paramsScroll, paramsInput);
+          copyFromInput = paramsInput;
+          currentMode = &paramsEditMode;
+
+          Rectangle updateUrlBtn = { textArea.x + 30, textArea.y + textArea.height - 10, 120, 20 };
+          // TODO: Automatic update
+          if (GuiButton(updateUrlBtn, "Update URL"))
+          {
+            char tempUrl[1024];
+            strncpy(tempUrl, urlInput, sizeof(tempUrl) - 1);
+
+            // Remove existing params if any
+            char *questionMark = strchr(tempUrl, '?');
+            if (questionMark) *questionMark = '\0';
+
+            updateUrlWithParams(urlInput, sizeof(urlInput), tempUrl, paramsInput);
+          }
+          break;
+        }
+      }
+
+      if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C))
+      {
+        SetClipboardText(copyFromInput);
+      }
+
+      Rectangle editBtn = { textArea.x + textArea.width - 60, textArea.y + textArea.height - 10, 50, 20 };
+      if (GuiButton(editBtn, "Edit"))
+      {
+        *currentMode = !(*currentMode);
+      }
+
+      // Response Panel with scroll
+      GuiGroupBox(responsePanel, "Response");
+
+      Rectangle responseArea = {
+        responsePanel.x + 10,
+        responsePanel.y + 30,
+        responsePanel.width - 20,
+        responsePanel.height - 40
+      };
+
+      // Manual scroll for response
+      if (CheckCollisionPointRec(GetMousePosition(), responseArea))
+      {
+        float wheel = GetMouseWheelMove();
+        responseScroll.y += wheel * 20;
+        if (responseScroll.y < 0) responseScroll.y = 0;
+      }
+
+      // Draw border
+      DrawRectangleLinesEx(responseArea, 1, GRAY);
+
+      // Draw response text with scroll
+      BeginScissorMode(responseArea.x, responseArea.y, responseArea.width, responseArea.height);
+
+      int yPos = responseArea.y + 5 - (int)responseScroll.y;
+      char *textCopy = strdup(responseText);
+      char *line = strtok(textCopy, "\n");
+
+      while (line != NULL && yPos < responseArea.y + responseArea.height)
+      {
+        if (yPos + LINE_HEIGHT > responseArea.y)
+        {
+          DrawText(line, responseArea.x + 5, yPos, TEXT_SIZE, DARKGRAY);
+        }
+        yPos += LINE_HEIGHT;
+        line = strtok(NULL, "\n");
+      }
+      free(textCopy);
+
+      EndScissorMode();
+
+      if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C))
+      {
+        if (CheckCollisionPointRec(GetMousePosition(), responseArea)) {
+          SetClipboardText(responseText);
+        }
+      }
+
+      // ---  Edit modal  ----
+      if (jsonEditMode)
+      {
+        GuiTextBox(
+            (Rectangle){ screenWidth/2 - 300, screenHeight/2 - 200, 600, 400 },
+            jsonInput, JSON_INPUT_BUFFER_LEN, true);
+      }
+
+      if (headersEditMode)
+      {
+        GuiTextBox(
+            (Rectangle){ screenWidth/2 - 300, screenHeight/2 - 200, 600, 400 },
+            headersInput, HEADER_INPUT_BUFFER_LEN, true);
+      }
+
+      if (paramsEditMode)
+      {
+        GuiTextBox(
+            (Rectangle){ screenWidth/2 - 300, screenHeight/2 - 200, 600, 400 },
+            paramsInput, PARAM_INPUT_BUFFER_LEN, true);
+      }
+
+      if (GuiDropdownBox(methodButton, "GET;POST;PUT;DELETE", &methodActive, methodDropdown))
+      {
+        methodDropdown = !methodDropdown;
+      }
+
+
+    EndDrawing();
+  }
+
+  CloseWindow();
+  return 0;
+}
--- a/raylib_examples/BUILD	Tue Dec 16 21:01:45 2025 -0500
+++ b/raylib_examples/BUILD	Sat Dec 20 13:56:01 2025 -0500
@@ -1,4 +1,4 @@
-load(":raylib.bzl", "raylib_binary")
+load("//third_party/raylib:raylib.bzl", "raylib_binary")
 
 raylib_binary(
   name = "hello_world",
--- a/raylib_examples/raylib.bzl	Tue Dec 16 21:01:45 2025 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-def raylib_binary(
-        name,
-        srcs,
-        deps = [],
-        deps_macos = [],
-        deps_linux = [],
-        linkopts_macos = [
-          "-framework CoreVideo",
-          "-framework IOKit",
-          "-framework Cocoa",
-          "-framework GLUT",
-          "-framework OpenGL",
-        ],
-        linkopts_linux = [
-          "-lGL",
-          "-lm",
-          "-lpthread",
-          "-ldl",
-          "-lrt",
-          "-lX11",
-        ]):
-    """
-    Raylib specific cross platform rules.
-
-    Args:
-        name: The logical name of the binary (alias).
-        srcs: List of source files (common).
-        deps: Mutual dependency.
-        deps_macos: Extra deps for macOS.
-        deps_linux: Extra deps for Linux.
-        linkopts_macos: Extra linkopts for macOS.
-        linkopts_linux: Extra linkopts for Linux.
-    """
-
-    macos_bin = name + "_macos"
-    linux_bin = name + "_linux"
-
-    native.cc_binary(
-        name = macos_bin,
-        srcs = srcs,
-        deps = deps + deps_macos,
-        linkopts = linkopts_macos,
-    )
-
-    native.cc_binary(
-        name = linux_bin,
-        srcs = srcs,
-        deps = deps + deps_linux,
-        linkopts = linkopts_linux,
-    )
-
-    native.alias(
-        name = name,
-        actual = select({
-            "//config:macos": ":" + macos_bin,
-            "//config:linux": ":" + linux_bin,
-            "//conditions:default": ":" + linux_bin,  # fallback
-        }),
-    )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/react_games/src/random.tsx	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,105 @@
+import { InputEvent, useCallback, useEffect, useRef, useState } from "react";
+import ReactDOM from "react-dom/client";
+
+// 1. input and listen to its key stroke fire findSuggestions
+// 2. debounce findSuggestions
+// 3. create structure for its to search
+
+const DEBOUNCER_WAIT_TIME = 500;
+
+const WORDS = [
+  "cat",
+  "call",
+  "cell",
+  "car",
+  "carpet",
+  "celcius"
+]
+
+interface Tries {
+  children: Record<string, Tries>;
+  words: Set<string>;
+}
+
+function TypeAhead() {
+  const inputRef = useRef<HTMLInputElement | null>(null);
+  const debounceTimerId = useRef<NodeJS.Timeout>(null);
+  const tries = useRef<Tries>({ children: {}, words: new Set(WORDS) })
+  const [suggestions, setSuggestions] = useState<string[]>([]);
+
+  const performSearch = useCallback((value: string) => {
+    let curr = tries.current
+    const res: string[] = []
+    function dfs(node: Tries, pos: number, off: number = 0, matching: number = 0) {
+      if (off > 2) {
+        if (matching > Math.ceil(value.length/2)) {
+          node.words.forEach(word => res.push(word));
+        }
+        return;
+      }
+
+      if (pos == value.length) {
+        node.words.forEach(word => res.push(word));
+        return;
+      }
+
+      const letter = value[pos];
+      if (!node.children[letter]) {
+        Object.keys(node.children).forEach((key) => {
+          dfs(node.children[key], pos+1, off+1, matching)
+        });
+      } else {
+        dfs(node.children[letter], pos+1, off, matching + 1)
+      }
+
+    }
+    dfs(curr, 0);
+    setSuggestions([...res]);
+  }, [])
+
+  const findSuggestions = useCallback(() => {
+    if (debounceTimerId.current) {
+      clearTimeout(debounceTimerId.current);
+    }
+    debounceTimerId.current = setTimeout(() => performSearch(inputRef.current?.value || ""), DEBOUNCER_WAIT_TIME)
+  }, [])
+
+  useEffect(() => {
+    WORDS.forEach(word => {
+      let curr = tries.current;
+      for (let letter of word) {
+        letter = letter.toLowerCase();
+        if (!curr.children[letter]) {
+          curr.children[letter] = { children: {}, words: new Set([word]) };
+        } else {
+          curr.children[letter].words.add(word) ;
+        }
+        curr = curr.children[letter];
+      }
+    })
+    return () => {
+      if (debounceTimerId.current)
+        clearTimeout(debounceTimerId.current)
+    }
+  }, [])
+
+  return (
+    <>
+      <h1>  Type ahead </h1>
+      <input ref={inputRef} onChange={findSuggestions}/>
+      {suggestions && (
+        <div style={{display: "flex", flexDirection: "column", gap: 8, border: "1px black solid"}}>
+        {suggestions.map(suggestion => {
+          return (
+            <div>{suggestion}</div>
+          )
+        })}
+        </div>
+      )}
+    </>
+  );
+}
+
+// Render
+const root = ReactDOM.createRoot(document.getElementById("root")!);
+root.render(<TypeAhead />);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/third_party/raylib/include/raygui.h	Sat Dec 20 13:56:01 2025 -0500
@@ -0,0 +1,6123 @@
+/*******************************************************************************************
+*
+*   raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library
+*
+*   DESCRIPTION:
+*       raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
+*       available as a standalone library, as long as input and drawing functions are provided
+*
+*   FEATURES:
+*       - Immediate-mode gui, minimal retained data
+*       - +25 controls provided (basic and advanced)
+*       - Styling system for colors, font and metrics
+*       - Icons supported, embedded as a 1-bit icons pack
+*       - Standalone mode option (custom input/graphics backend)
+*       - Multiple support tools provided for raygui development
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Better standalone mode API for easy plug of custom backends
+*       - Externalize required inputs, allow user easier customization
+*
+*   LIMITATIONS:
+*       - No editable multi-line word-wraped text box supported
+*       - No auto-layout mechanism, up to the user to define controls position and size
+*       - Standalone mode requires library modification and some user work to plug another backend
+*
+*   NOTES:
+*       - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
+*         font atlas recs and glyphs, freeing that memory is (usually) up to the user,
+*         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
+*         by default any previously loaded font (texture, recs, glyphs)
+*       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
+*
+*   CONTROLS PROVIDED:
+*     # Container/separators Controls
+*       - WindowBox     --> StatusBar, Panel
+*       - GroupBox      --> Line
+*       - Line
+*       - Panel         --> StatusBar
+*       - ScrollPanel   --> StatusBar
+*       - TabBar        --> Button
+*
+*     # Basic Controls
+*       - Label
+*       - LabelButton   --> Label
+*       - Button
+*       - Toggle
+*       - ToggleGroup   --> Toggle
+*       - ToggleSlider
+*       - CheckBox
+*       - ComboBox
+*       - DropdownBox
+*       - TextBox
+*       - ValueBox      --> TextBox
+*       - Spinner       --> Button, ValueBox
+*       - Slider
+*       - SliderBar     --> Slider
+*       - ProgressBar
+*       - StatusBar
+*       - DummyRec
+*       - Grid
+*
+*     # Advance Controls
+*       - ListView
+*       - ColorPicker   --> ColorPanel, ColorBarHue
+*       - MessageBox    --> Window, Label, Button
+*       - TextInputBox  --> Window, Label, TextBox, Button
+*
+*     It also provides a set of functions for styling the controls based on its properties (size, color)
+*
+*
+*   RAYGUI STYLE (guiStyle):
+*       raygui uses a global data array for all gui style properties (allocated on data segment by default),
+*       when a new style is loaded, it is loaded over the global style... but a default gui style could always be
+*       recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one
+*
+*       The global style array size is fixed and depends on the number of controls and properties:
+*
+*           static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
+*
+*       guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
+*
+*       Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
+*       used for all controls, when any of those base values is set, it is automatically populated to all
+*       controls, so, specific control values overwriting generic style should be set after base values
+*
+*       After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
+*       properties are actually common to all controls and can not be overwritten individually (like BASE ones)
+*       Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR
+*
+*       Custom control properties can be defined using the EXTENDED properties for each independent control.
+*
+*       TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler
+*
+*
+*   RAYGUI ICONS (guiIcons):
+*       raygui could use a global array containing icons data (allocated on data segment by default),
+*       a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set
+*       must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded
+*
+*       Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
+*       requires 8 integers (16*16/32) to be stored in memory.
+*
+*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
+*
+*       The global icons array size is fixed and depends on the number of icons and size:
+*
+*           static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS];
+*
+*       guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+*
+*       TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons
+*
+*   RAYGUI LAYOUT:
+*       raygui currently does not provide an auto-layout mechanism like other libraries,
+*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
+*
+*       TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
+*
+*   CONFIGURATION:
+*       #define RAYGUI_IMPLEMENTATION
+*           Generates the implementation of the library into the included file
+*           If not defined, the library is in header only mode and can be included in other headers
+*           or source files without problems. But only ONE file should hold the implementation
+*
+*       #define RAYGUI_STANDALONE
+*           Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
+*           internally in the library and input management and drawing functions must be provided by
+*           the user (check library implementation for further details)
+*
+*       #define RAYGUI_NO_ICONS
+*           Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
+*
+*       #define RAYGUI_CUSTOM_ICONS
+*           Includes custom ricons.h header defining a set of custom icons,
+*           this file can be generated using rGuiIcons tool
+*
+*       #define RAYGUI_DEBUG_RECS_BOUNDS
+*           Draw control bounds rectangles for debug
+*
+*       #define RAYGUI_DEBUG_TEXT_BOUNDS
+*           Draw text bounds rectangles for debug
+*
+*   VERSIONS HISTORY:
+*       5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
+*                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+*                         ADDED: GuiValueBoxFloat()
+*                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+*                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: GuiLoadIconsFromMemory()
+*                         ADDED: Multiple new icons
+*                         REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+*                         REMOVED: GuiSliderPro(), functionality was redundant
+*                         REVIEWED: Controls using text labels to use LABEL properties
+*                         REVIEWED: Replaced sprintf() by snprintf() for more safety
+*                         REVIEWED: GuiTabBar(), close tab with mouse middle button
+*                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+*                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+*                         REVIEWED: GuiTextBox(), cursor position initialization
+*                         REVIEWED: GuiSliderPro(), control value change check
+*                         REVIEWED: GuiGrid(), simplified implementation
+*                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+*                         REVIEWED: GuiDrawText(), improved wrap mode drawing
+*                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: GuiProgressBar(), improved borders computing
+*                         REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
+*                         REVIEWED: Functions descriptions, removed wrong return value reference
+*                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
+*
+*       4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
+*                         ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
+*                         ADDED: Multiple new icons, mostly compiler related
+*                         ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE
+*                         ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode
+*                         ADDED: Support loading styles with custom font charset from external file
+*                         REDESIGNED: GuiTextBox(), support mouse cursor positioning
+*                         REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only)
+*                         REDESIGNED: GuiProgressBar() to be more visual, progress affects border color
+*                         REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText()
+*                         REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value
+*                         REDESIGNED: GuiComboBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiCheckBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSlider(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSliderBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiProgressBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiListView(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPicker(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), added extra parameter
+*                         REDESIGNED: GuiListViewEx(), change parameters order
+*                         REDESIGNED: All controls return result as int value
+*                         REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars
+*                         REVIEWED: All examples and specially controls_test_suite
+*                         RENAMED: gui_file_dialog module to gui_window_file_dialog
+*                         UPDATED: All styles to include ISO-8859-15 charset (as much as possible)
+*
+*       3.6 (10-May-2023) ADDED: New icon: SAND_TIMER
+*                         ADDED: GuiLoadStyleFromMemory() (binary only)
+*                         REVIEWED: GuiScrollBar() horizontal movement key
+*                         REVIEWED: GuiTextBox() crash on cursor movement
+*                         REVIEWED: GuiTextBox(), additional inputs support
+*                         REVIEWED: GuiLabelButton(), avoid text cut
+*                         REVIEWED: GuiTextInputBox(), password input
+*                         REVIEWED: Local GetCodepointNext(), aligned with raylib
+*                         REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds
+*
+*       3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle()
+*                         ADDED: Helper functions to split text in separate lines
+*                         ADDED: Multiple new icons, useful for code editing tools
+*                         REMOVED: Unneeded icon editing functions
+*                         REMOVED: GuiTextBoxMulti(), very limited and broken
+*                         REMOVED: MeasureTextEx() dependency, logic directly implemented
+*                         REMOVED: DrawTextEx() dependency, logic directly implemented
+*                         REVIEWED: GuiScrollBar(), improve mouse-click behaviour
+*                         REVIEWED: Library header info, more info, better organized
+*                         REDESIGNED: GuiTextBox() to support cursor movement
+*                         REDESIGNED: GuiDrawText() to divide drawing by lines
+*
+*       3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes
+*                         REMOVED: GuiScrollBar(), only internal
+*                         REDESIGNED: GuiPanel() to support text parameter
+*                         REDESIGNED: GuiScrollPanel() to support text parameter
+*                         REDESIGNED: GuiColorPicker() to support text parameter
+*                         REDESIGNED: GuiColorPanel() to support text parameter
+*                         REDESIGNED: GuiColorBarAlpha() to support text parameter
+*                         REDESIGNED: GuiColorBarHue() to support text parameter
+*                         REDESIGNED: GuiTextInputBox() to support password
+*
+*       3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool)
+*                         REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures
+*                         REVIEWED: External icons usage logic
+*                         REVIEWED: GuiLine() for centered alignment when including text
+*                         RENAMED: Multiple controls properties definitions to prepend RAYGUI_
+*                         RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency
+*                         Projects updated and multiple tweaks
+*
+*       3.0 (04-Nov-2021) Integrated ricons data to avoid external file
+*                         REDESIGNED: GuiTextBoxMulti()
+*                         REMOVED: GuiImageButton*()
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.9 (17-Mar-2021) REMOVED: Tooltip API
+*       2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle()
+*       2.7 (20-Feb-2020) ADDED: Possible tooltips API
+*       2.6 (09-Sep-2019) ADDED: GuiTextInputBox()
+*                         REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox()
+*                         REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle()
+*                         Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties
+*                         ADDED: 8 new custom styles ready to use
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
+*       2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed
+*                         Refactor all controls drawing mechanism to use control state
+*       2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls
+*       2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string
+*                         REDESIGNED: Style system (breaking change)
+*       2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts
+*                         REVIEWED: GuiComboBox(), GuiListView()...
+*       1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()...
+*       1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout
+*       1.5 (21-Jun-2017) Working in an improved styles system
+*       1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
+*       1.3 (12-Jun-2017) Complete redesign of style system
+*       1.1 (01-Jun-2017) Complete review of the library
+*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
+*
+*   DEPENDENCIES:
+*       raylib 5.6-dev  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+*
+*   STANDALONE MODE:
+*       By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
+*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
+*
+*       The following functions should be redefined for a custom backend:
+*
+*           - Vector2 GetMousePosition(void);
+*           - float GetMouseWheelMove(void);
+*           - bool IsMouseButtonDown(int button);
+*           - bool IsMouseButtonPressed(int button);
+*           - bool IsMouseButtonReleased(int button);
+*           - bool IsKeyDown(int key);
+*           - bool IsKeyPressed(int key);
+*           - int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+*
+*           - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle()
+*           - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+*
+*           - Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+*           - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle()
+*           - Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+*           - void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+*           - char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+*           - void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+*           - const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+*           - int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+*           - void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+*           - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+*
+*   CONTRIBUTORS:
+*       Ramon Santamaria:   Supervision, review, redesign, update and maintenance
+*       Vlad Adrian:        Complete rewrite of GuiTextBox() to support extended features (2019)
+*       Sergio Martinez:    Review, testing (2015) and redesign of multiple controls (2018)
+*       Adria Arranz:       Testing and implementation of additional controls (2018)
+*       Jordi Jorba:        Testing and implementation of additional controls (2018)
+*       Albert Martos:      Review and testing of the library (2015)
+*       Ian Eito:           Review and testing of the library (2015)
+*       Kevin Gato:         Initial implementation of basic components (2014)
+*       Daniel Nicolas:     Initial implementation of basic components (2014)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RAYGUI_H
+#define RAYGUI_H
+
+#define RAYGUI_VERSION_MAJOR 4
+#define RAYGUI_VERSION_MINOR 5
+#define RAYGUI_VERSION_PATCH 0
+#define RAYGUI_VERSION  "5.0-dev"
+
+#if !defined(RAYGUI_STANDALONE)
+    #include "raylib.h"
+#endif
+
+// Function specifiers in case library is build/used as a shared library (Windows)
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+#if defined(_WIN32)
+    #if defined(BUILD_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+    #elif defined(USE_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+    #endif
+#endif
+
+// Function specifiers definition
+#ifndef RAYGUIAPI
+    #define RAYGUIAPI       // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define RAYGUI_SUPPORT_LOG_INFO
+#if defined(RAYGUI_SUPPORT_LOG_INFO)
+  #define RAYGUI_LOG(...)           printf(__VA_ARGS__)
+#else
+  #define RAYGUI_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+// NOTE: Some types are required for RAYGUI_STANDALONE usage
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+    #ifndef __cplusplus
+    // Boolean type
+        #ifndef true
+            typedef enum { false, true } bool;
+        #endif
+    #endif
+
+    // Vector2 type
+    typedef struct Vector2 {
+        float x;
+        float y;
+    } Vector2;
+
+    // Vector3 type                 // -- ConvertHSVtoRGB(), ConvertRGBtoHSV()
+    typedef struct Vector3 {
+        float x;
+        float y;
+        float z;
+    } Vector3;
+
+    // Color type, RGBA (32bit)
+    typedef struct Color {
+        unsigned char r;
+        unsigned char g;
+        unsigned char b;
+        unsigned char a;
+    } Color;
+
+    // Rectangle type
+    typedef struct Rectangle {
+        float x;
+        float y;
+        float width;
+        float height;
+    } Rectangle;
+
+    // TODO: Texture2D type is very coupled to raylib, required by Font type
+    // It should be redesigned to be provided by user
+    typedef struct Texture {
+        unsigned int id;        // OpenGL texture id
+        int width;              // Texture base width
+        int height;             // Texture base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Texture;
+
+    // Texture2D, same as Texture
+    typedef Texture Texture2D;
+
+    // Image, pixel data stored in CPU memory (RAM)
+    typedef struct Image {
+        void *data;             // Image raw data
+        int width;              // Image base width
+        int height;             // Image base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Image;
+
+    // GlyphInfo, font characters glyphs info
+    typedef struct GlyphInfo {
+        int value;              // Character value (Unicode)
+        int offsetX;            // Character offset X when drawing
+        int offsetY;            // Character offset Y when drawing
+        int advanceX;           // Character advance position X
+        Image image;            // Character image data
+    } GlyphInfo;
+
+    // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle()
+    // It should be redesigned to be provided by user
+    typedef struct Font {
+        int baseSize;           // Base size (default chars height)
+        int glyphCount;         // Number of glyph characters
+        int glyphPadding;       // Padding around the glyph characters
+        Texture2D texture;      // Texture atlas containing the glyphs
+        Rectangle *recs;        // Rectangles in texture for the glyphs
+        GlyphInfo *glyphs;      // Glyphs info data
+    } Font;
+#endif
+
+// Style property
+// NOTE: Used when exporting style as code for convenience
+typedef struct GuiStyleProp {
+    unsigned short controlId;   // Control identifier
+    unsigned short propertyId;  // Property identifier
+    int propertyValue;          // Property value
+} GuiStyleProp;
+
+/*
+// Controls text style -NOT USED-
+// NOTE: Text style is defined by control
+typedef struct GuiTextStyle {
+    unsigned int size;
+    int charSpacing;
+    int lineSpacing;
+    int alignmentH;
+    int alignmentV;
+    int padding;
+} GuiTextStyle;
+*/
+
+// Gui control state
+typedef enum {
+    STATE_NORMAL = 0,
+    STATE_FOCUSED,
+    STATE_PRESSED,
+    STATE_DISABLED
+} GuiState;
+
+// Gui control text alignment
+typedef enum {
+    TEXT_ALIGN_LEFT = 0,
+    TEXT_ALIGN_CENTER,
+    TEXT_ALIGN_RIGHT
+} GuiTextAlignment;
+
+// Gui control text alignment vertical
+// NOTE: Text vertical position inside the text bounds
+typedef enum {
+    TEXT_ALIGN_TOP = 0,
+    TEXT_ALIGN_MIDDLE,
+    TEXT_ALIGN_BOTTOM
+} GuiTextAlignmentVertical;
+
+// Gui control text wrap mode
+// NOTE: Useful for multiline text
+typedef enum {
+    TEXT_WRAP_NONE = 0,
+    TEXT_WRAP_CHAR,
+    TEXT_WRAP_WORD
+} GuiTextWrapMode;
+
+// Gui controls
+typedef enum {
+    // Default -> populates to all controls when set
+    DEFAULT = 0,
+
+    // Basic controls
+    LABEL,          // Used also for: LABELBUTTON
+    BUTTON,
+    TOGGLE,         // Used also for: TOGGLEGROUP
+    SLIDER,         // Used also for: SLIDERBAR, TOGGLESLIDER
+    PROGRESSBAR,
+    CHECKBOX,
+    COMBOBOX,
+    DROPDOWNBOX,
+    TEXTBOX,        // Used also for: TEXTBOXMULTI
+    VALUEBOX,
+    CONTROL11,
+    LISTVIEW,
+    COLORPICKER,
+    SCROLLBAR,
+    STATUSBAR
+} GuiControl;
+
+// Gui base properties for every control
+// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
+typedef enum {
+    BORDER_COLOR_NORMAL = 0,    // Control border color in STATE_NORMAL
+    BASE_COLOR_NORMAL,          // Control base color in STATE_NORMAL
+    TEXT_COLOR_NORMAL,          // Control text color in STATE_NORMAL
+    BORDER_COLOR_FOCUSED,       // Control border color in STATE_FOCUSED
+    BASE_COLOR_FOCUSED,         // Control base color in STATE_FOCUSED
+    TEXT_COLOR_FOCUSED,         // Control text color in STATE_FOCUSED
+    BORDER_COLOR_PRESSED,       // Control border color in STATE_PRESSED
+    BASE_COLOR_PRESSED,         // Control base color in STATE_PRESSED
+    TEXT_COLOR_PRESSED,         // Control text color in STATE_PRESSED
+    BORDER_COLOR_DISABLED,      // Control border color in STATE_DISABLED
+    BASE_COLOR_DISABLED,        // Control base color in STATE_DISABLED
+    TEXT_COLOR_DISABLED,        // Control text color in STATE_DISABLED
+    BORDER_WIDTH = 12,          // Control border size, 0 for no border
+    //TEXT_SIZE,                  // Control text size (glyphs max height) -> GLOBAL for all controls
+    //TEXT_SPACING,               // Control text spacing between glyphs -> GLOBAL for all controls
+    //TEXT_LINE_SPACING,          // Control text spacing between lines -> GLOBAL for all controls
+    TEXT_PADDING = 13,          // Control text padding, not considering border
+    TEXT_ALIGNMENT = 14,        // Control text horizontal alignment inside control text bound (after border and padding)
+    //TEXT_WRAP_MODE              // Control text wrap-mode inside text bounds -> GLOBAL for all controls
+} GuiControlProperty;
+
+// TODO: Which text styling properties should be global or per-control?
+// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while
+// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and
+// should be configured by user as needed while defining the UI layout
+
+// Gui extended properties depend on control
+// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
+//----------------------------------------------------------------------------------
+// DEFAULT extended properties
+// NOTE: Those properties are common to all controls or global
+// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT?
+typedef enum {
+    TEXT_SIZE = 16,             // Text size (glyphs max height)
+    TEXT_SPACING,               // Text spacing between glyphs
+    LINE_COLOR,                 // Line control color
+    BACKGROUND_COLOR,           // Background color
+    TEXT_LINE_SPACING,          // Text spacing between lines
+    TEXT_ALIGNMENT_VERTICAL,    // Text vertical alignment inside text bounds (after border and padding)
+    TEXT_WRAP_MODE              // Text wrap-mode inside text bounds
+    //TEXT_DECORATION             // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline
+    //TEXT_DECORATION_THICK       // Text decoration line thickness
+} GuiDefaultProperty;
+
+// Other possible text properties:
+// TEXT_WEIGHT                  // Normal, Italic, Bold -> Requires specific font change
+// TEXT_INDENT                  // Text indentation -> Now using TEXT_PADDING...
+
+// Label
+//typedef enum { } GuiLabelProperty;
+
+// Button/Spinner
+//typedef enum { } GuiButtonProperty;
+
+// Toggle/ToggleGroup
+typedef enum {
+    GROUP_PADDING = 16,         // ToggleGroup separation between toggles
+} GuiToggleProperty;
+
+// Slider/SliderBar
+typedef enum {
+    SLIDER_WIDTH = 16,          // Slider size of internal bar
+    SLIDER_PADDING              // Slider/SliderBar internal bar padding
+} GuiSliderProperty;
+
+// ProgressBar
+typedef enum {
+    PROGRESS_PADDING = 16,      // ProgressBar internal padding
+} GuiProgressBarProperty;
+
+// ScrollBar
+typedef enum {
+    ARROWS_SIZE = 16,           // ScrollBar arrows size
+    ARROWS_VISIBLE,             // ScrollBar arrows visible
+    SCROLL_SLIDER_PADDING,      // ScrollBar slider internal padding
+    SCROLL_SLIDER_SIZE,         // ScrollBar slider size
+    SCROLL_PADDING,             // ScrollBar scroll padding from arrows
+    SCROLL_SPEED,               // ScrollBar scrolling speed
+} GuiScrollBarProperty;
+
+// CheckBox
+typedef enum {
+    CHECK_PADDING = 16          // CheckBox internal check padding
+} GuiCheckBoxProperty;
+
+// ComboBox
+typedef enum {
+    COMBO_BUTTON_WIDTH = 16,    // ComboBox right button width
+    COMBO_BUTTON_SPACING        // ComboBox button separation
+} GuiComboBoxProperty;
+
+// DropdownBox
+typedef enum {
+    ARROW_PADDING = 16,         // DropdownBox arrow separation from border and items
+    DROPDOWN_ITEMS_SPACING,     // DropdownBox items separation
+    DROPDOWN_ARROW_HIDDEN,      // DropdownBox arrow hidden
+    DROPDOWN_ROLL_UP            // DropdownBox roll up flag (default rolls down)
+} GuiDropdownBoxProperty;
+
+// TextBox/TextBoxMulti/ValueBox/Spinner
+typedef enum {
+    TEXT_READONLY = 16,         // TextBox in read-only mode: 0-text editable, 1-text no-editable
+} GuiTextBoxProperty;
+
+// ValueBox/Spinner
+typedef enum {
+    SPINNER_BUTTON_WIDTH = 16,  // Spinner left/right buttons width
+    SPINNER_BUTTON_SPACING,     // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
+
+// ListView
+typedef enum {
+    LIST_ITEMS_HEIGHT = 16,     // ListView items height
+    LIST_ITEMS_SPACING,         // ListView items separation
+    SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
+    SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_NORMAL,   // ListView items border enabled in normal state
+    LIST_ITEMS_BORDER_WIDTH     // ListView items border width
+} GuiListViewProperty;
+
+// ColorPicker
+typedef enum {
+    COLOR_SELECTOR_SIZE = 16,
+    HUEBAR_WIDTH,               // ColorPicker right hue bar width
+    HUEBAR_PADDING,             // ColorPicker right hue bar separation from panel
+    HUEBAR_SELECTOR_HEIGHT,     // ColorPicker right hue bar selector height
+    HUEBAR_SELECTOR_OVERFLOW    // ColorPicker right hue bar selector overflow
+} GuiColorPickerProperty;
+
+#define SCROLLBAR_LEFT_SIDE     0
+#define SCROLLBAR_RIGHT_SIDE    1
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+// Global gui state control functions
+RAYGUIAPI void GuiEnable(void);                                 // Enable gui controls (global state)
+RAYGUIAPI void GuiDisable(void);                                // Disable gui controls (global state)
+RAYGUIAPI void GuiLock(void);                                   // Lock gui controls (global state)
+RAYGUIAPI void GuiUnlock(void);                                 // Unlock gui controls (global state)
+RAYGUIAPI bool GuiIsLocked(void);                               // Check if gui is locked (global state)
+RAYGUIAPI void GuiSetAlpha(float alpha);                        // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f
+RAYGUIAPI void GuiSetState(int state);                          // Set gui state (global state)
+RAYGUIAPI int GuiGetState(void);                                // Get gui state (global state)
+
+// Font set/get functions
+RAYGUIAPI void GuiSetFont(Font font);                           // Set gui custom font (global state)
+RAYGUIAPI Font GuiGetFont(void);                                // Get gui custom font (global state)
+
+// Style set/get functions
+RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property
+RAYGUIAPI int GuiGetStyle(int control, int property);           // Get one style property
+
+// Styles loading functions
+RAYGUIAPI void GuiLoadStyle(const char *fileName);              // Load style file over global style variable (.rgs)
+RAYGUIAPI void GuiLoadStyleDefault(void);                       // Load style default over global style
+
+// Tooltips management functions
+RAYGUIAPI void GuiEnableTooltip(void);                          // Enable gui tooltips (global state)
+RAYGUIAPI void GuiDisableTooltip(void);                         // Disable gui tooltips (global state)
+RAYGUIAPI void GuiSetTooltip(const char *tooltip);              // Set tooltip string
+
+// Icons functionality
+RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported)
+#if !defined(RAYGUI_NO_ICONS)
+RAYGUIAPI void GuiSetIconScale(int scale);                      // Set default icon drawing size
+RAYGUIAPI unsigned int *GuiGetIcons(void);                      // Get raygui icons data pointer
+RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data
+RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
+#endif
+
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text);                // Get text width considering gui style and icon size (if required)
+
+// Controls
+//----------------------------------------------------------------------------------------------------------
+// Container/separator controls, useful for controls organization
+RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title);                                       // Window Box control, shows a window that can be closed
+RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text);                                         // Group Box control with text name
+RAYGUIAPI int GuiLine(Rectangle bounds, const char *text);                                             // Line separator control, could contain text
+RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text);                                            // Panel control, useful to group controls
+RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active);                  // Tab Bar control, returns TAB to be closed or -1
+RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control
+
+// Basic controls set
+RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text);                                            // Label control
+RAYGUIAPI int GuiButton(Rectangle bounds, const char *text);                                           // Button control, returns true when clicked
+RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text);                                      // Label button control, returns true when clicked
+RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active);                             // Toggle Button control
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active);                         // Toggle Group control
+RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active);                        // Toggle Slider control
+RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked);                          // Check Box control, returns true when active
+RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active);                            // Combo Box control
+
+RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode);          // Dropdown Box control
+RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control
+RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
+RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values
+RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode);                   // Text Box control, updates input text
+
+RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control
+RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control
+RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control
+RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text);                                        // Status Bar control, shows info text
+RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text);                                         // Dummy control for placeholders
+RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control
+
+// Advance controls set
+RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active);          // List View control
+RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters
+RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message
+RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret
+RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color);                        // Color Picker control (multiple color controls)
+RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color);                         // Color Panel control
+RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha);                      // Color Bar Alpha control
+RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value);                        // Color Bar Hue control
+RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                // Color Picker control that avoids conversion to RGB on each call (multiple color controls)
+RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                 // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV()
+//----------------------------------------------------------------------------------------------------------
+
+#if !defined(RAYGUI_NO_ICONS)
+
+#if !defined(RAYGUI_CUSTOM_ICONS)
+//----------------------------------------------------------------------------------
+// Icons enumeration
+//----------------------------------------------------------------------------------
+typedef enum {
+    ICON_NONE                     = 0,
+    ICON_FOLDER_FILE_OPEN         = 1,
+    ICON_FILE_SAVE_CLASSIC        = 2,
+    ICON_FOLDER_OPEN              = 3,
+    ICON_FOLDER_SAVE              = 4,
+    ICON_FILE_OPEN                = 5,
+    ICON_FILE_SAVE                = 6,
+    ICON_FILE_EXPORT              = 7,
+    ICON_FILE_ADD                 = 8,
+    ICON_FILE_DELETE              = 9,
+    ICON_FILETYPE_TEXT            = 10,
+    ICON_FILETYPE_AUDIO           = 11,
+    ICON_FILETYPE_IMAGE           = 12,
+    ICON_FILETYPE_PLAY            = 13,
+    ICON_FILETYPE_VIDEO           = 14,
+    ICON_FILETYPE_INFO            = 15,
+    ICON_FILE_COPY                = 16,
+    ICON_FILE_CUT                 = 17,
+    ICON_FILE_PASTE               = 18,
+    ICON_CURSOR_HAND              = 19,
+    ICON_CURSOR_POINTER           = 20,
+    ICON_CURSOR_CLASSIC           = 21,
+    ICON_PENCIL                   = 22,
+    ICON_PENCIL_BIG               = 23,
+    ICON_BRUSH_CLASSIC            = 24,
+    ICON_BRUSH_PAINTER            = 25,
+    ICON_WATER_DROP               = 26,
+    ICON_COLOR_PICKER             = 27,
+    ICON_RUBBER                   = 28,
+    ICON_COLOR_BUCKET             = 29,
+    ICON_TEXT_T                   = 30,
+    ICON_TEXT_A                   = 31,
+    ICON_SCALE                    = 32,
+    ICON_RESIZE                   = 33,
+    ICON_FILTER_POINT             = 34,
+    ICON_FILTER_BILINEAR          = 35,
+    ICON_CROP                     = 36,
+    ICON_CROP_ALPHA               = 37,
+    ICON_SQUARE_TOGGLE            = 38,
+    ICON_SYMMETRY                 = 39,
+    ICON_SYMMETRY_HORIZONTAL      = 40,
+    ICON_SYMMETRY_VERTICAL        = 41,
+    ICON_LENS                     = 42,
+    ICON_LENS_BIG                 = 43,
+    ICON_EYE_ON                   = 44,
+    ICON_EYE_OFF                  = 45,
+    ICON_FILTER_TOP               = 46,
+    ICON_FILTER                   = 47,
+    ICON_TARGET_POINT             = 48,
+    ICON_TARGET_SMALL             = 49,
+    ICON_TARGET_BIG               = 50,
+    ICON_TARGET_MOVE              = 51,
+    ICON_CURSOR_MOVE              = 52,
+    ICON_CURSOR_SCALE             = 53,
+    ICON_CURSOR_SCALE_RIGHT       = 54,
+    ICON_CURSOR_SCALE_LEFT        = 55,
+    ICON_UNDO                     = 56,
+    ICON_REDO                     = 57,
+    ICON_REREDO                   = 58,
+    ICON_MUTATE                   = 59,
+    ICON_ROTATE                   = 60,
+    ICON_REPEAT                   = 61,
+    ICON_SHUFFLE                  = 62,
+    ICON_EMPTYBOX                 = 63,
+    ICON_TARGET                   = 64,
+    ICON_TARGET_SMALL_FILL        = 65,
+    ICON_TARGET_BIG_FILL          = 66,
+    ICON_TARGET_MOVE_FILL         = 67,
+    ICON_CURSOR_MOVE_FILL         = 68,
+    ICON_CURSOR_SCALE_FILL        = 69,
+    ICON_CURSOR_SCALE_RIGHT_FILL  = 70,
+    ICON_CURSOR_SCALE_LEFT_FILL   = 71,
+    ICON_UNDO_FILL                = 72,
+    ICON_REDO_FILL                = 73,
+    ICON_REREDO_FILL              = 74,
+    ICON_MUTATE_FILL              = 75,
+    ICON_ROTATE_FILL              = 76,
+    ICON_REPEAT_FILL              = 77,
+    ICON_SHUFFLE_FILL             = 78,
+    ICON_EMPTYBOX_SMALL           = 79,
+    ICON_BOX                      = 80,
+    ICON_BOX_TOP                  = 81,
+    ICON_BOX_TOP_RIGHT            = 82,
+    ICON_BOX_RIGHT                = 83,
+    ICON_BOX_BOTTOM_RIGHT         = 84,
+    ICON_BOX_BOTTOM               = 85,
+    ICON_BOX_BOTTOM_LEFT          = 86,
+    ICON_BOX_LEFT                 = 87,
+    ICON_BOX_TOP_LEFT             = 88,
+    ICON_BOX_CENTER               = 89,
+    ICON_BOX_CIRCLE_MASK          = 90,
+    ICON_POT                      = 91,
+    ICON_ALPHA_MULTIPLY           = 92,
+    ICON_ALPHA_CLEAR              = 93,
+    ICON_DITHERING                = 94,
+    ICON_MIPMAPS                  = 95,
+    ICON_BOX_GRID                 = 96,
+    ICON_GRID                     = 97,
+    ICON_BOX_CORNERS_SMALL        = 98,
+    ICON_BOX_CORNERS_BIG          = 99,
+    ICON_FOUR_BOXES               = 100,
+    ICON_GRID_FILL                = 101,
+    ICON_BOX_MULTISIZE            = 102,
+    ICON_ZOOM_SMALL               = 103,
+    ICON_ZOOM_MEDIUM              = 104,
+    ICON_ZOOM_BIG                 = 105,
+    ICON_ZOOM_ALL                 = 106,
+    ICON_ZOOM_CENTER              = 107,
+    ICON_BOX_DOTS_SMALL           = 108,
+    ICON_BOX_DOTS_BIG             = 109,
+    ICON_BOX_CONCENTRIC           = 110,
+    ICON_BOX_GRID_BIG             = 111,
+    ICON_OK_TICK                  = 112,
+    ICON_CROSS                    = 113,
+    ICON_ARROW_LEFT               = 114,
+    ICON_ARROW_RIGHT              = 115,
+    ICON_ARROW_DOWN               = 116,
+    ICON_ARROW_UP                 = 117,
+    ICON_ARROW_LEFT_FILL          = 118,
+    ICON_ARROW_RIGHT_FILL         = 119,
+    ICON_ARROW_DOWN_FILL          = 120,
+    ICON_ARROW_UP_FILL            = 121,
+    ICON_AUDIO                    = 122,
+    ICON_FX                       = 123,
+    ICON_WAVE                     = 124,
+    ICON_WAVE_SINUS               = 125,
+    ICON_WAVE_SQUARE              = 126,
+    ICON_WAVE_TRIANGULAR          = 127,
+    ICON_CROSS_SMALL              = 128,
+    ICON_PLAYER_PREVIOUS          = 129,
+    ICON_PLAYER_PLAY_BACK         = 130,
+    ICON_PLAYER_PLAY              = 131,
+    ICON_PLAYER_PAUSE             = 132,
+    ICON_PLAYER_STOP              = 133,
+    ICON_PLAYER_NEXT              = 134,
+    ICON_PLAYER_RECORD            = 135,
+    ICON_MAGNET                   = 136,
+    ICON_LOCK_CLOSE               = 137,
+    ICON_LOCK_OPEN                = 138,
+    ICON_CLOCK                    = 139,
+    ICON_TOOLS                    = 140,
+    ICON_GEAR                     = 141,
+    ICON_GEAR_BIG                 = 142,
+    ICON_BIN                      = 143,
+    ICON_HAND_POINTER             = 144,
+    ICON_LASER                    = 145,
+    ICON_COIN                     = 146,
+    ICON_EXPLOSION                = 147,
+    ICON_1UP                      = 148,
+    ICON_PLAYER                   = 149,
+    ICON_PLAYER_JUMP              = 150,
+    ICON_KEY                      = 151,
+    ICON_DEMON                    = 152,
+    ICON_TEXT_POPUP               = 153,
+    ICON_GEAR_EX                  = 154,
+    ICON_CRACK                    = 155,
+    ICON_CRACK_POINTS             = 156,
+    ICON_STAR                     = 157,
+    ICON_DOOR                     = 158,
+    ICON_EXIT                     = 159,
+    ICON_MODE_2D                  = 160,
+    ICON_MODE_3D                  = 161,
+    ICON_CUBE                     = 162,
+    ICON_CUBE_FACE_TOP            = 163,
+    ICON_CUBE_FACE_LEFT           = 164,
+    ICON_CUBE_FACE_FRONT          = 165,
+    ICON_CUBE_FACE_BOTTOM         = 166,
+    ICON_CUBE_FACE_RIGHT          = 167,
+    ICON_CUBE_FACE_BACK           = 168,
+    ICON_CAMERA                   = 169,
+    ICON_SPECIAL                  = 170,
+    ICON_LINK_NET                 = 171,
+    ICON_LINK_BOXES               = 172,
+    ICON_LINK_MULTI               = 173,
+    ICON_LINK                     = 174,
+    ICON_LINK_BROKE               = 175,
+    ICON_TEXT_NOTES               = 176,
+    ICON_NOTEBOOK                 = 177,
+    ICON_SUITCASE                 = 178,
+    ICON_SUITCASE_ZIP             = 179,
+    ICON_MAILBOX                  = 180,
+    ICON_MONITOR                  = 181,
+    ICON_PRINTER                  = 182,
+    ICON_PHOTO_CAMERA             = 183,
+    ICON_PHOTO_CAMERA_FLASH       = 184,
+    ICON_HOUSE                    = 185,
+    ICON_HEART                    = 186,
+    ICON_CORNER                   = 187,
+    ICON_VERTICAL_BARS            = 188,
+    ICON_VERTICAL_BARS_FILL       = 189,
+    ICON_LIFE_BARS                = 190,
+    ICON_INFO                     = 191,
+    ICON_CROSSLINE                = 192,
+    ICON_HELP                     = 193,
+    ICON_FILETYPE_ALPHA           = 194,
+    ICON_FILETYPE_HOME            = 195,
+    ICON_LAYERS_VISIBLE           = 196,
+    ICON_LAYERS                   = 197,
+    ICON_WINDOW                   = 198,
+    ICON_HIDPI                    = 199,
+    ICON_FILETYPE_BINARY          = 200,
+    ICON_HEX                      = 201,
+    ICON_SHIELD                   = 202,
+    ICON_FILE_NEW                 = 203,
+    ICON_FOLDER_ADD               = 204,
+    ICON_ALARM                    = 205,
+    ICON_CPU                      = 206,
+    ICON_ROM                      = 207,
+    ICON_STEP_OVER                = 208,
+    ICON_STEP_INTO                = 209,
+    ICON_STEP_OUT                 = 210,
+    ICON_RESTART                  = 211,
+    ICON_BREAKPOINT_ON            = 212,
+    ICON_BREAKPOINT_OFF           = 213,
+    ICON_BURGER_MENU              = 214,
+    ICON_CASE_SENSITIVE           = 215,
+    ICON_REG_EXP                  = 216,
+    ICON_FOLDER                   = 217,
+    ICON_FILE                     = 218,
+    ICON_SAND_TIMER               = 219,
+    ICON_WARNING                  = 220,
+    ICON_HELP_BOX                 = 221,
+    ICON_INFO_BOX                 = 222,
+    ICON_PRIORITY                 = 223,
+    ICON_LAYERS_ISO               = 224,
+    ICON_LAYERS2                  = 225,
+    ICON_MLAYERS                  = 226,
+    ICON_MAPS                     = 227,
+    ICON_HOT                      = 228,
+    ICON_LABEL                    = 229,
+    ICON_NAME_ID                  = 230,
+    ICON_SLICING                  = 231,
+    ICON_MANUAL_CONTROL           = 232,
+    ICON_COLLISION                = 233,
+    ICON_CIRCLE_ADD               = 234,
+    ICON_CIRCLE_ADD_FILL          = 235,
+    ICON_CIRCLE_WARNING           = 236,
+    ICON_CIRCLE_WARNING_FILL      = 237,
+    ICON_BOX_MORE                 = 238,
+    ICON_BOX_MORE_FILL            = 239,
+    ICON_BOX_MINUS                = 240,
+    ICON_BOX_MINUS_FILL           = 241,
+    ICON_UNION                    = 242,
+    ICON_INTERSECTION             = 243,
+    ICON_DIFFERENCE               = 244,
+    ICON_SPHERE                   = 245,
+    ICON_CYLINDER                 = 246,
+    ICON_CONE                     = 247,
+    ICON_ELLIPSOID                = 248,
+    ICON_CAPSULE                  = 249,
+    ICON_250                      = 250,
+    ICON_251                      = 251,
+    ICON_252                      = 252,
+    ICON_253                      = 253,
+    ICON_254                      = 254,
+    ICON_255                      = 255
+} GuiIconName;
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+}            // Prevents name mangling of functions
+#endif
+
+#endif // RAYGUI_H
+
+/***********************************************************************************
+*
+*   RAYGUI IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RAYGUI_IMPLEMENTATION)
+
+#include <ctype.h>              // required for: isspace() [GuiTextBox()]
+#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
+#include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
+#include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
+#include <math.h>               // Required for: roundf() [GuiColorPicker()]
+
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+        #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+    #endif
+#else
+    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+    #define RAYGUI_MALLOC(sz)       malloc(sz)
+    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
+    #define RAYGUI_FREE(p)          free(p)
+#endif
+
+#ifdef __cplusplus
+    #define RAYGUI_CLITERAL(name) name
+#else
+    #define RAYGUI_CLITERAL(name) (name)
+#endif
+
+// Check if two rectangles are equal, used to validate a slider bounds as an id
+#ifndef CHECK_BOUNDS_ID
+    #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
+#endif
+
+#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
+
+// Embedded icons, no external file provided
+#define RAYGUI_ICON_SIZE               16          // Size of icons in pixels (squared)
+#define RAYGUI_ICON_MAX_ICONS         256          // Maximum number of icons
+#define RAYGUI_ICON_MAX_NAME_LENGTH    32          // Maximum length of icon name id
+
+// Icons data is defined by bit array (every bit represents one pixel)
+// Those arrays are stored as unsigned int data arrays, so,
+// every array element defines 32 pixels (bits) of information
+// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels)
+// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels)
+#define RAYGUI_ICON_DATA_ELEMENTS   (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32)
+
+//----------------------------------------------------------------------------------
+// Icons data for all gui possible icons (allocated on data segment by default)
+//
+// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so,
+// every 16x16 icon requires 8 integers (16*16/32) to be stored
+//
+// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(),
+// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS
+//
+// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = {
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_NONE
+    0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe,      // ICON_FOLDER_FILE_OPEN
+    0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe,      // ICON_FILE_SAVE_CLASSIC
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100,      // ICON_FOLDER_OPEN
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000,      // ICON_FOLDER_SAVE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc,      // ICON_FILE_OPEN
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc,      // ICON_FILE_SAVE
+    0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc,      // ICON_FILE_EXPORT
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc,      // ICON_FILE_ADD
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc,      // ICON_FILE_DELETE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_TEXT
+    0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_AUDIO
+    0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_IMAGE
+    0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_PLAY
+    0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4,      // ICON_FILETYPE_VIDEO
+    0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc,      // ICON_FILETYPE_INFO
+    0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0,      // ICON_FILE_COPY
+    0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000,      // ICON_FILE_CUT
+    0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0,      // ICON_FILE_PASTE
+    0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_CURSOR_HAND
+    0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000,      // ICON_CURSOR_POINTER
+    0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000,      // ICON_CURSOR_CLASSIC
+    0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000,      // ICON_PENCIL
+    0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000,      // ICON_PENCIL_BIG
+    0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8,      // ICON_BRUSH_CLASSIC
+    0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080,      // ICON_BRUSH_PAINTER
+    0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000,      // ICON_WATER_DROP
+    0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000,      // ICON_COLOR_PICKER
+    0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000,      // ICON_RUBBER
+    0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040,      // ICON_COLOR_BUCKET
+    0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000,      // ICON_TEXT_T
+    0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f,      // ICON_TEXT_A
+    0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e,      // ICON_SCALE
+    0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe,      // ICON_RESIZE
+    0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_POINT
+    0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_BILINEAR
+    0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002,      // ICON_CROP
+    0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000,      // ICON_CROP_ALPHA
+    0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002,      // ICON_SQUARE_TOGGLE
+    0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000,      // ICON_SYMMETRY
+    0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100,      // ICON_SYMMETRY_HORIZONTAL
+    0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180,      // ICON_SYMMETRY_VERTICAL
+    0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000,      // ICON_LENS
+    0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000,      // ICON_LENS_BIG
+    0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000,      // ICON_EYE_ON
+    0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000,      // ICON_EYE_OFF
+    0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100,      // ICON_FILTER_TOP
+    0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0,      // ICON_FILTER
+    0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_POINT
+    0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL
+    0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG
+    0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280,      // ICON_TARGET_MOVE
+    0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280,      // ICON_CURSOR_MOVE
+    0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e,      // ICON_CURSOR_SCALE
+    0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT
+    0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000,      // ICON_CURSOR_SCALE_LEFT
+    0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO
+    0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000,      // ICON_REREDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000,      // ICON_MUTATE
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020,      // ICON_ROTATE
+    0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000,      // ICON_REPEAT
+    0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000,      // ICON_SHUFFLE
+    0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe,      // ICON_EMPTYBOX
+    0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000,      // ICON_TARGET
+    0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL_FILL
+    0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380,      // ICON_TARGET_MOVE_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380,      // ICON_CURSOR_MOVE_FILL
+    0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e,      // ICON_CURSOR_SCALE_FILL
+    0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT_FILL
+    0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000,      // ICON_CURSOR_SCALE_LEFT_FILL
+    0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO_FILL
+    0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000,      // ICON_REREDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000,      // ICON_MUTATE_FILL
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020,      // ICON_ROTATE_FILL
+    0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000,      // ICON_REPEAT_FILL
+    0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000,      // ICON_SHUFFLE_FILL
+    0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000,      // ICON_EMPTYBOX_SMALL
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX
+    0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP
+    0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000,      // ICON_BOX_BOTTOM_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000,      // ICON_BOX_BOTTOM
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000,      // ICON_BOX_BOTTOM_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_LEFT
+    0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_CENTER
+    0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe,      // ICON_BOX_CIRCLE_MASK
+    0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff,      // ICON_POT
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe,      // ICON_ALPHA_MULTIPLY
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe,      // ICON_ALPHA_CLEAR
+    0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe,      // ICON_DITHERING
+    0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0,      // ICON_MIPMAPS
+    0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000,      // ICON_BOX_GRID
+    0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248,      // ICON_GRID
+    0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000,      // ICON_BOX_CORNERS_SMALL
+    0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e,      // ICON_BOX_CORNERS_BIG
+    0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000,      // ICON_FOUR_BOXES
+    0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000,      // ICON_GRID_FILL
+    0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e,      // ICON_BOX_MULTISIZE
+    0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_SMALL
+    0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_MEDIUM
+    0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e,      // ICON_ZOOM_BIG
+    0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e,      // ICON_ZOOM_ALL
+    0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000,      // ICON_ZOOM_CENTER
+    0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000,      // ICON_BOX_DOTS_SMALL
+    0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000,      // ICON_BOX_DOTS_BIG
+    0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe,      // ICON_BOX_CONCENTRIC
+    0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000,      // ICON_BOX_GRID_BIG
+    0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000,      // ICON_OK_TICK
+    0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000,      // ICON_CROSS
+    0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000,      // ICON_ARROW_LEFT
+    0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000,      // ICON_ARROW_RIGHT
+    0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN
+    0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP
+    0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000,      // ICON_ARROW_LEFT_FILL
+    0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000,      // ICON_ARROW_RIGHT_FILL
+    0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN_FILL
+    0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP_FILL
+    0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000,      // ICON_AUDIO
+    0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000,      // ICON_FX
+    0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000,      // ICON_WAVE
+    0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000,      // ICON_WAVE_SINUS
+    0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000,      // ICON_WAVE_SQUARE
+    0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000,      // ICON_WAVE_TRIANGULAR
+    0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000,      // ICON_CROSS_SMALL
+    0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000,      // ICON_PLAYER_PREVIOUS
+    0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000,      // ICON_PLAYER_PLAY_BACK
+    0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000,      // ICON_PLAYER_PLAY
+    0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000,      // ICON_PLAYER_PAUSE
+    0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000,      // ICON_PLAYER_STOP
+    0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000,      // ICON_PLAYER_NEXT
+    0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000,      // ICON_PLAYER_RECORD
+    0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000,      // ICON_MAGNET
+    0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_CLOSE
+    0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_OPEN
+    0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770,      // ICON_CLOCK
+    0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70,      // ICON_TOOLS
+    0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180,      // ICON_GEAR
+    0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180,      // ICON_GEAR_BIG
+    0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8,      // ICON_BIN
+    0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000,      // ICON_HAND_POINTER
+    0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000,      // ICON_LASER
+    0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000,      // ICON_COIN
+    0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000,      // ICON_EXPLOSION
+    0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000,      // ICON_1UP
+    0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240,      // ICON_PLAYER
+    0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000,      // ICON_PLAYER_JUMP
+    0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0,      // ICON_KEY
+    0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0,      // ICON_DEMON
+    0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000,      // ICON_TEXT_POPUP
+    0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000,      // ICON_GEAR_EX
+    0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK
+    0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK_POINTS
+    0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808,      // ICON_STAR
+    0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8,      // ICON_DOOR
+    0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0,      // ICON_EXIT
+    0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000,      // ICON_MODE_2D
+    0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000,      // ICON_MODE_3D
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE
+    0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_TOP
+    0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe,      // ICON_CUBE_FACE_LEFT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe,      // ICON_CUBE_FACE_FRONT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe,      // ICON_CUBE_FACE_BOTTOM
+    0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe,      // ICON_CUBE_FACE_RIGHT
+    0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_BACK
+    0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000,      // ICON_CAMERA
+    0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000,      // ICON_SPECIAL
+    0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800,      // ICON_LINK_NET
+    0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00,      // ICON_LINK_BOXES
+    0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000,      // ICON_LINK_MULTI
+    0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00,      // ICON_LINK
+    0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00,      // ICON_LINK_BROKE
+    0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_TEXT_NOTES
+    0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc,      // ICON_NOTEBOOK
+    0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000,      // ICON_SUITCASE
+    0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000,      // ICON_SUITCASE_ZIP
+    0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000,      // ICON_MAILBOX
+    0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000,      // ICON_MONITOR
+    0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000,      // ICON_PRINTER
+    0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA
+    0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA_FLASH
+    0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000,      // ICON_HOUSE
+    0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000,      // ICON_HEART
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000,      // ICON_CORNER
+    0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000,      // ICON_VERTICAL_BARS
+    0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000,      // ICON_VERTICAL_BARS_FILL
+    0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000,      // ICON_LIFE_BARS
+    0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc,      // ICON_INFO
+    0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002,      // ICON_CROSSLINE
+    0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000,      // ICON_HELP
+    0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc,      // ICON_FILETYPE_ALPHA
+    0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_HOME
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS_VISIBLE
+    0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS
+    0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_WINDOW
+    0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000,      // ICON_HIDPI
+    0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc,      // ICON_FILETYPE_BINARY
+    0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000,      // ICON_HEX
+    0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180,      // ICON_SHIELD
+    0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8,      // ICON_FILE_NEW
+    0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000,      // ICON_FOLDER_ADD
+    0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420,      // ICON_ALARM
+    0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0,      // ICON_CPU
+    0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0,      // ICON_ROM
+    0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000,      // ICON_STEP_OVER
+    0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_INTO
+    0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_OUT
+    0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000,      // ICON_RESTART
+    0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_ON
+    0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_OFF
+    0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000,      // ICON_BURGER_MENU
+    0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000,      // ICON_CASE_SENSITIVE
+    0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000,      // ICON_REG_EXP
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_FOLDER
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc,      // ICON_FILE
+    0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0,      // ICON_SAND_TIMER
+    0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc,      // ICON_WARNING
+    0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe,      // ICON_HELP_BOX
+    0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe,      // ICON_INFO_BOX
+    0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0,      // ICON_PRIORITY
+    0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000,      // ICON_LAYERS_ISO
+    0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
+    0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
+    0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060,      // ICON_LABEL
+    0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000,      // ICON_NAME_ID
+    0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000,      // ICON_SLICING
+    0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0,      // ICON_MANUAL_CONTROL
+    0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe,      // ICON_COLLISION
+    0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_ADD
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_ADD_FILL
+    0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_WARNING
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_WARNING_FILL
+    0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE
+    0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE_FILL
+    0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS
+    0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS_FILL
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0,      // ICON_UNION
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_INTERSECTION
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_DIFFERENCE
+    0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0,      // ICON_SPHERE
+    0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0,      // ICON_CYLINDER
+    0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0,      // ICON_CONE
+    0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000,      // ICON_ELLIPSOID
+    0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000,      // ICON_CAPSULE
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_250
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_251
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_252
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_253
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_254
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_255
+};
+
+// NOTE: A pointer to current icons array should be defined
+static unsigned int *guiIconsPtr = guiIcons;
+
+#endif      // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
+
+#ifndef RAYGUI_ICON_SIZE
+    #define RAYGUI_ICON_SIZE             0
+#endif
+
+// WARNING: Those values define the total size of the style data array,
+// if changed, previous saved styles could become incompatible
+#define RAYGUI_MAX_CONTROLS             16      // Maximum number of controls
+#define RAYGUI_MAX_PROPS_BASE           16      // Maximum number of base properties
+#define RAYGUI_MAX_PROPS_EXTENDED        8      // Maximum number of extended properties
+
+//----------------------------------------------------------------------------------
+// Module Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Gui control property style color element
+typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static GuiState guiState = STATE_NORMAL;        // Gui global state, if !STATE_NORMAL, forces defined state
+
+static Font guiFont = { 0 };                    // Gui current font (WARNING: highly coupled to raylib)
+static bool guiLocked = false;                  // Gui lock state (no inputs processed)
+static float guiAlpha = 1.0f;                   // Gui controls transparency
+
+static unsigned int guiIconScale = 1;           // Gui icon default scale (if icons enabled)
+
+static bool guiTooltip = false;                 // Tooltip enabled/disabled
+static const char *guiTooltipPtr = NULL;        // Tooltip string pointer (string provided by user)
+
+static bool guiControlExclusiveMode = false;    // Gui control exclusive mode (no inputs processed except current control)
+static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier
+
+static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
+//static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
+static int autoCursorCounter = 0;               // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
+
+//----------------------------------------------------------------------------------
+// Style data array for all gui style properties (allocated on data segment by default)
+//
+// NOTE 1: First set of BASE properties are generic to all controls but could be individually
+// overwritten per control, first set of EXTENDED properties are generic to all controls and
+// can not be overwritten individually but custom EXTENDED properties can be used by control
+//
+// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(),
+// but default gui style could always be recovered with GuiLoadStyleDefault()
+//
+// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
+
+static bool guiStyleLoaded = false;         // Style loaded flag for lazy style initialization
+
+//----------------------------------------------------------------------------------
+// Standalone Mode Functions Declaration
+//
+// NOTE: raygui depend on some raylib input and drawing functions
+// To use raygui as standalone library, below functions must be defined by the user
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+
+#define KEY_RIGHT           262
+#define KEY_LEFT            263
+#define KEY_DOWN            264
+#define KEY_UP              265
+#define KEY_BACKSPACE       259
+#define KEY_ENTER           257
+
+#define MOUSE_LEFT_BUTTON     0
+
+// Input required functions
+//-------------------------------------------------------------------------------
+static Vector2 GetMousePosition(void);
+static float GetMouseWheelMove(void);
+static bool IsMouseButtonDown(int button);
+static bool IsMouseButtonPressed(int button);
+static bool IsMouseButtonReleased(int button);
+
+static bool IsKeyDown(int key);
+static bool IsKeyPressed(int key);
+static int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+//-------------------------------------------------------------------------------
+
+// Drawing required functions
+//-------------------------------------------------------------------------------
+static void DrawRectangle(int x, int y, int width, int height, Color color);        // -- GuiDrawRectangle()
+static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+//-------------------------------------------------------------------------------
+
+// Text required functions
+//-------------------------------------------------------------------------------
+static Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font
+
+static Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+static void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+
+static char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+static void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+
+static const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+
+static int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+static void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+
+static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+//-------------------------------------------------------------------------------
+
+// raylib functions already implemented in raygui
+//-------------------------------------------------------------------------------
+static Color GetColor(int hexValue);                // Returns a Color struct from hexadecimal value
+static int ColorToInt(Color color);                 // Returns hexadecimal value for a Color
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec);   // Check if point is inside rectangle
+static const char *TextFormat(const char *text, ...);               // Formatting of text with variables to 'embed'
+static const char **TextSplit(const char *text, char delimiter, int *count);    // Split text into multiple strings
+static int TextToInteger(const char *text);         // Get integer value from text
+static float TextToFloat(const char *text);         // Get float value from text
+
+static int GetCodepointNext(const char *text, int *codepointSize);  // Get next codepoint in a UTF-8 encoded text
+static const char *CodepointToUTF8(int codepoint, int *byteSize);   // Encode codepoint into UTF-8 text (char array size returned as parameter)
+
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);  // Draw rectangle vertical gradient
+//-------------------------------------------------------------------------------
+
+#endif      // RAYGUI_STANDALONE
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize);    // Load style from memory (binary only)
+
+static Rectangle GetTextBounds(int control, Rectangle bounds);  // Get text bounds considering control bounds
+static const char *GetTextIcon(const char *text, int *iconId);  // Get text icon if provided and move text cursor
+
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint);     // Gui draw text using default font
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color);   // Gui draw rectangle using default raygui style
+
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow);   // Split controls text into multiple strings
+static Vector3 ConvertHSVtoRGB(Vector3 hsv);                    // Convert color data from HSV to RGB
+static Vector3 ConvertRGBtoHSV(Vector3 rgb);                    // Convert color data from RGB to HSV
+
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue);   // Scroll bar control, used by GuiScrollPanel()
+static void GuiTooltip(Rectangle controlRec);                   // Draw tooltip using control rec position
+
+static Color GuiFade(Color color, float alpha);         // Fade color by an alpha factor
+
+//----------------------------------------------------------------------------------
+// Gui Setup Functions Definition
+//----------------------------------------------------------------------------------
+// Enable gui global state
+// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups
+void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; }
+
+// Disable gui global state
+// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups
+void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; }
+
+// Lock gui global state
+void GuiLock(void) { guiLocked = true; }
+
+// Unlock gui global state
+void GuiUnlock(void) { guiLocked = false; }
+
+// Check if gui is locked (global state)
+bool GuiIsLocked(void) { return guiLocked; }
+
+// Set gui controls alpha global state
+void GuiSetAlpha(float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    guiAlpha = alpha;
+}
+
+// Set gui state (global state)
+void GuiSetState(int state) { guiState = (GuiState)state; }
+
+// Get gui state (global state)
+int GuiGetState(void) { return guiState; }
+
+// Set custom gui font
+// NOTE: Font loading/unloading is external to raygui
+void GuiSetFont(Font font)
+{
+    if (font.texture.id > 0)
+    {
+        // NOTE: If we try to setup a font but default style has not been
+        // lazily loaded before, it will be overwritten, so we need to force
+        // default style loading first
+        if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+        guiFont = font;
+    }
+}
+
+// Get custom gui font
+Font GuiGetFont(void)
+{
+    return guiFont;
+}
+
+// Set control style property value
+void GuiSetStyle(int control, int property, int value)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+
+    // Default properties are propagated to all controls
+    if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE))
+    {
+        for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+    }
+}
+
+// Get control style property value
+int GuiGetStyle(int control, int property)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property];
+}
+
+//----------------------------------------------------------------------------------
+// Gui Controls Functions Definition
+//----------------------------------------------------------------------------------
+
+// Window Box control
+int GuiWindowBox(Rectangle bounds, const char *title)
+{
+    // Window title bar height (including borders)
+    // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox()
+    #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT)
+        #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT        24
+    #endif
+
+    #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+        #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT      18
+    #endif
+
+    int result = 0;
+    //GuiState state = guiState;
+
+    int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT;
+
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
+    if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+
+    const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
+    Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
+    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+                                 statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
+
+    // Update control
+    //--------------------------------------------------------------------
+    // NOTE: Logic is directly managed by button
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiStatusBar(statusBar, title); // Draw window header as status bar
+    GuiPanel(windowPanel, NULL);    // Draw window base
+
+    // Draw window close button
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+    result = GuiButton(closeButtonRec, "x");
+#else
+    result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL));
+#endif
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Window close button clicked: result = 1
+}
+
+// Group Box control with text name
+int GuiGroupBox(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_GROUPBOX_LINE_THICK)
+        #define RAYGUI_GROUPBOX_LINE_THICK     1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+
+    GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Line control
+int GuiLine(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_LINE_MARGIN_TEXT)
+        #define RAYGUI_LINE_MARGIN_TEXT  12
+    #endif
+    #if !defined(RAYGUI_LINE_TEXT_PADDING)
+        #define RAYGUI_LINE_TEXT_PADDING  4
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR));
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color);
+    else
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = bounds.height;
+        textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
+        textBounds.y = bounds.y;
+
+        // Draw line with embedded text label: "--- text --------------"
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+        GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color);
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Panel control
+int GuiPanel(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_PANEL_BORDER_WIDTH)
+        #define RAYGUI_PANEL_BORDER_WIDTH   1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+    }
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Tab Bar control
+// NOTE: Using GuiToggle() for the TABS
+int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
+{
+    #define RAYGUI_TABBAR_ITEM_WIDTH    148
+
+    int result = -1;
+    //GuiState state = guiState;
+
+    Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height };
+
+    if (*active < 0) *active = 0;
+    else if (*active > count - 1) *active = count - 1;
+
+    int offsetX = 0;    // Required in case tabs go out of screen
+    offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth();
+    if (offsetX < 0) offsetX = 0;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Draw control
+    //--------------------------------------------------------------------
+    for (int i = 0; i < count; i++)
+    {
+        tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX;
+
+        if (tabBounds.x < GetScreenWidth())
+        {
+            // Draw tabs as toggle controls
+            int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT);
+            int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+            GuiSetStyle(TOGGLE, TEXT_PADDING, 8);
+
+            if (i == (*active))
+            {
+                toggle = true;
+                GuiToggle(tabBounds, text[i], &toggle);
+            }
+            else
+            {
+                toggle = false;
+                GuiToggle(tabBounds, text[i], &toggle);
+                if (toggle) *active = i;
+            }
+
+            // Close tab with middle mouse button pressed
+            if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i;
+
+            GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
+
+            // Draw tab close button
+            // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds))
+            int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+            int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+            GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i;
+#else
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i;
+#endif
+            GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+        }
+    }
+
+    // Draw tab-bar bottom line
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL)));
+    //--------------------------------------------------------------------
+
+    return result;     // Return as result the current TAB closing requested
+}
+
+// Scroll Panel control
+int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view)
+{
+    #define RAYGUI_MIN_SCROLLBAR_WIDTH     40
+    #define RAYGUI_MIN_SCROLLBAR_HEIGHT    40
+    #define RAYGUI_MIN_MOUSE_WHEEL_SPEED   20
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Rectangle temp = { 0 };
+    if (view == NULL) view = &temp;
+
+    Vector2 scrollPos = { 0.0f, 0.0f };
+    if (scroll != NULL) scrollPos = *scroll;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1;
+    }
+
+    bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+    bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+
+    // Recheck to account for the other scrollbar being visible
+    if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+    if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+
+    int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    int verticalScrollBarWidth =  hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    Rectangle horizontalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)horizontalScrollBarWidth
+    };
+    Rectangle verticalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)),
+        (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)verticalScrollBarWidth,
+        (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+    };
+
+    // Make sure scroll bars have a minimum width/height
+    if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
+    if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
+
+    // Calculate view area (area without the scrollbars)
+    *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } :
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth };
+
+    // Clip view area to the actual content size
+    if (view->width > content.width) view->width = content.width;
+    if (view->height > content.height) view->height = content.height;
+
+    float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f;
+    float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+#if defined(SUPPORT_SCROLLBAR_KEY_INPUT)
+            if (hasHorizontalScrollBar)
+            {
+                if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+
+            if (hasVerticalScrollBar)
+            {
+                if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+#endif
+            float wheelMove = GetMouseWheelMove();
+
+            // Set scrolling speed with mouse wheel based on ratio between bounds and content
+            Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height };
+            if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+            if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+
+            // Horizontal and vertical scrolling with mouse wheel
+            if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x;
+            else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll
+        }
+    }
+
+    // Normalize scroll values
+    if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin;
+    if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax;
+    if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin;
+    if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));        // Draw background
+
+    // Save size of the scrollbar slider
+    const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+
+    // Draw horizontal scrollbar if visible
+    if (hasHorizontalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content width and the widget width
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)));
+        scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax);
+    }
+    else scrollPos.x = 0.0f;
+
+    // Draw vertical scrollbar if visible
+    if (hasVerticalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content height and the widget height
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)));
+        scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax);
+    }
+    else scrollPos.y = 0.0f;
+
+    // Draw detail corner rectangle if both scroll bars are visible
+    if (hasHorizontalScrollBar && hasVerticalScrollBar)
+    {
+        Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 };
+        GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))));
+    }
+
+    // Draw scrollbar lines depending on current state
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
+
+    // Set scrollbar slider size back to the way it was before
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider);
+    //--------------------------------------------------------------------
+
+    if (scroll != NULL) *scroll = scrollPos;
+
+    return result;
+}
+
+// Label control
+int GuiLabel(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    //...
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Button control, returns true when clicked
+int GuiButton(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))));
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //------------------------------------------------------------------
+
+    return result;      // Button pressed: result = 1
+}
+
+// Label button control
+int GuiLabelButton(Rectangle bounds, const char *text)
+{
+    GuiState state = guiState;
+    bool pressed = false;
+
+    // NOTE: We force bounds.width to be all text
+    float textWidth = (float)GuiGetTextWidth(text);
+    if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return pressed;
+}
+
+// Toggle Button control
+int GuiToggle(Rectangle bounds, const char *text, bool *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (active == NULL) active = &temp;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check toggle button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_NORMAL;
+                *active = !(*active);
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_NORMAL)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))));
+    }
+    else
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3)));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)));
+    }
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Toggle Group control
+int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
+{
+    #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
+        #define RAYGUI_TOGGLEGROUP_MAX_ITEMS    32
+    #endif
+
+    int result = 0;
+    float initBoundsX = bounds.x;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 };
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, rows);
+
+    int prevRow = rows[0];
+
+    for (int i = 0; i < itemCount; i++)
+    {
+        if (prevRow != rows[i])
+        {
+            bounds.x = initBoundsX;
+            bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING));
+            prevRow = rows[i];
+        }
+
+        if (i == (*active))
+        {
+            toggle = true;
+            GuiToggle(bounds, items[i], &toggle);
+        }
+        else
+        {
+            toggle = false;
+            GuiToggle(bounds, items[i], &toggle);
+            if (toggle) *active = i;
+        }
+
+        bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING));
+    }
+
+    return result;
+}
+
+// Toggle Slider control extended
+int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    //bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle slider = {
+        0,      // Calculated later depending on the active toggle
+        bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+        (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount,
+        bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                (*active)++;
+                result = 1;
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED;
+    }
+
+    if (*active >= itemCount) *active = 0;
+    slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))),
+        GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL)));
+
+    // Draw internal slider
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+
+    // Draw text in slider
+    if (text != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Check Box control, returns 1 when state changed
+int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (checked == NULL) checked = &temp;
+
+    Rectangle textBounds = { 0 };
+
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        Rectangle totalBounds = {
+            (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x,
+            bounds.y,
+            bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING),
+            bounds.height,
+        };
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, totalBounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                *checked = !(*checked);
+                result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK);
+
+    if (*checked)
+    {
+        Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)),
+                            bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) };
+        GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)));
+    }
+
+    GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Combo Box control
+int GuiComboBox(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING));
+
+    Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING),
+                           (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height };
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    if (*active < 0) *active = 0;
+    else if (*active > (itemCount - 1)) *active = itemCount - 1;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds) ||
+            CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                *active += 1;
+                if (*active >= itemCount) *active = 0;      // Cyclic combobox
+            }
+
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw combo box main
+    GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))));
+    GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))));
+
+    // Draw selector using a custom button
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount));
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dropdown Box control
+// NOTE: Returns mouse click
+int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    int itemSelected = *active;
+    int itemFocused = -1;
+
+    int direction = 0; // Dropdown box open direction: down (default)
+    if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle boundsOpen = bounds;
+    boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+    if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
+
+    Rectangle itemBounds = bounds;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            // Check if mouse has been pressed or released outside limits
+            if (!CheckCollisionPointRec(mousePoint, boundsOpen))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+
+            // Check if already selected item has been pressed again
+            if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+
+            // Check focused and selected item
+            for (int i = 0; i < itemCount; i++)
+            {
+                // Update item rectangle y position for next item
+                if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = i;
+                    if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+                    {
+                        itemSelected = i;
+                        result = 1;         // Item selected
+                    }
+                    break;
+                }
+            }
+
+            itemBounds = bounds;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    result = 1;
+                    state = STATE_PRESSED;
+                }
+                else state = STATE_FOCUSED;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (editMode) GuiPanel(boundsOpen, NULL);
+
+    GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)));
+    GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)));
+
+    if (editMode)
+    {
+        // Draw visible items
+        for (int i = 0; i < itemCount; i++)
+        {
+            // Update item rectangle y position for next item
+            if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+            if (i == itemSelected)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)));
+            }
+            else if (i == itemFocused)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)));
+            }
+            else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)));
+        }
+    }
+
+    if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN))
+    {
+        // Draw arrows (using icon if available)
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    *active = itemSelected;
+
+    // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item...
+    return result;   // Mouse click: result = 1
+}
+
+// Text Box control
+// NOTE: Returns true on ENTER pressed (useful for data validation)
+int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  20        // Frames to wait for autocursor movement
+    #endif
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY      1        // Frames delay for autocursor movement
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    bool multiline = true; // TODO: Consider multiline text input
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
+
+    Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
+    int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
+    int thisCursorIndex = textBoxCursorIndex;
+    if (thisCursorIndex > textLength) thisCursorIndex = textLength;
+
+    // Calculate cursor position for multiline
+    int cursorLine = 0;  // Current line number (0-based)
+    int lineStart = 0;   // Start index of current line
+
+    if (multiline)
+    {
+        // Count newlines before cursor to determine line number
+        for (int i = 0; i < thisCursorIndex; i++)
+        {
+            if (text[i] == '\n')
+            {
+                cursorLine++;
+                lineStart = i + 1;
+            }
+        }
+    }
+
+    // Calculate horizontal position within current line
+    char lineText[1024] = { 0 };
+    int lineTextLen = 0;
+    if (multiline)
+    {
+        // Extract current line text up to cursor
+        int i = lineStart;
+        while (i < thisCursorIndex && text[i] != '\n' && lineTextLen < 1023)
+        {
+            lineText[lineTextLen++] = text[i++];
+        }
+        lineText[lineTextLen] = '\0';
+    }
+
+    int textWidth = multiline ? GuiGetTextWidth(lineText) : (GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex));
+    int textIndexOffset = 0; // Text index offset to start drawing in the box
+
+    // Line height for multiline (matches GuiDrawText line spacing)
+    int lineHeight = GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+    // Cursor rectangle
+    // NOTE: Position X and Y values updated for multiline support
+    Rectangle cursor = {
+        textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
+        multiline ? (textBounds.y + cursorLine * lineHeight) : (textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)),
+        2,
+        (float)GuiGetStyle(DEFAULT, TEXT_SIZE)
+    };
+
+    if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+    if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
+
+    // Mouse cursor rectangle
+    // NOTE: Initialized outside of screen
+    Rectangle mouseCursor = cursor;
+    mouseCursor.x = -1;
+    mouseCursor.width = 1;
+
+    // Blink-cursor frame counter
+    //if (!autoCursorMode) blinkCursorFrameCounter++;
+    //else blinkCursorFrameCounter = 0;
+
+    // Update control
+    //--------------------------------------------------------------------
+    // WARNING: Text editing is only supported under certain conditions:
+    if ((state != STATE_DISABLED) &&                // Control not disabled
+        !GuiGetStyle(TEXTBOX, TEXT_READONLY) &&     // TextBox not on read-only mode
+        !guiLocked &&                               // Gui not locked
+        !guiControlExclusiveMode &&                       // No gui slider on dragging
+        (wrapMode == TEXT_WRAP_NONE))               // No wrap mode
+    {
+        Vector2 mousePosition = GetMousePosition();
+
+        if (editMode)
+        {
+            // GLOBAL: Auto-cursor movement logic
+            // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+            if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+            else autoCursorCounter = 0;
+
+            bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
+            state = STATE_PRESSED;
+
+            if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+
+            // If text does not fit in the textbox and current cursor position is out of bounds,
+            // we add an index offset to text for drawing only what requires depending on cursor
+            while (textWidth >= textBounds.width)
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textIndexOffset, &nextCodepointSize);
+
+                textIndexOffset += nextCodepointSize;
+
+                textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
+            }
+
+            int codepoint = GetCharPressed();       // Get Unicode codepoint
+            if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
+
+            // Encode codepoint as UTF-8
+            int codepointSize = 0;
+            const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
+
+            // Handle text paste action
+            if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                const char *pasteText = GetClipboardText();
+                if (pasteText != NULL)
+                {
+                    int pasteLength = 0;
+                    int pasteCodepoint;
+                    int pasteCodepointSize;
+
+                    // Count how many codepoints to copy, stopping at the first unwanted control character
+                    while (true)
+                    {
+                        pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+                        if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+                        if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+                        pasteLength += pasteCodepointSize;
+                    }
+
+                    if (pasteLength > 0)
+                    {
+                        // Move forward data from cursor position
+                        for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+                        // Paste data in at cursor
+                        for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+                        textBoxCursorIndex += pasteLength;
+                        textLength += pasteLength;
+                        text[textLength] = '\0';
+                    }
+                }
+            }
+            else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            {
+                // Adding codepoint to text, at current cursor position
+
+                // Move forward data from cursor position
+                for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
+
+                // Add new codepoint in current cursor position
+                for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
+
+                textBoxCursorIndex += codepointSize;
+                textLength += codepointSize;
+
+                // Make sure text last character is EOL
+                text[textLength] = '\0';
+            }
+
+            // Move cursor to start
+            if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0;
+
+            // Move cursor to end
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
+
+            // Delete related codepoints from text, after current cursor position
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+                        break;
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to delete (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+            }
+
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, after current cursor position
+
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+                textLength -= nextCodepointSize;
+            }
+
+            // Delete related codepoints from text, before current cursor position
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int prevCodepointSize = 0;
+                int prevCodepoint = 0;
+
+                // Check whitespace to delete (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+                textBoxCursorIndex -= accCodepointSize;
+            }
+
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, before current cursor position
+
+                int prevCodepointSize = 0;
+
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
+
+                textLength -= prevCodepointSize;
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+
+            // Move cursor position with keys
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int prevCodepointSize = 0;
+                int prevCodepoint = 0;
+
+                // Check whitespace to skip (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
+            {
+                int prevCodepointSize = 0;
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+            else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to skip (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                textBoxCursorIndex += nextCodepointSize;
+            }
+
+            // Vertical cursor movement for multiline
+            if (multiline && (IsKeyPressed(KEY_UP) || (IsKeyDown(KEY_UP) && autoCursorShouldTrigger)))
+            {
+                // Find start of current line
+                int currentLineStart = textBoxCursorIndex;
+                while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--;
+
+                // Calculate horizontal position in current line
+                int horizontalPos = textBoxCursorIndex - currentLineStart;
+
+                // Find start of previous line
+                if (currentLineStart > 0)
+                {
+                    int prevLineEnd = currentLineStart - 1; // Skip the newline
+                    int prevLineStart = prevLineEnd;
+                    while (prevLineStart > 0 && text[prevLineStart - 1] != '\n') prevLineStart--;
+
+                    // Move to same horizontal position on previous line (or end of line if shorter)
+                    int prevLineLength = prevLineEnd - prevLineStart;
+                    int targetPos = (horizontalPos < prevLineLength) ? horizontalPos : prevLineLength;
+                    textBoxCursorIndex = prevLineStart + targetPos;
+                }
+                else
+                {
+                    // Already on first line, move to start
+                    textBoxCursorIndex = 0;
+                }
+            }
+            else if (multiline && (IsKeyPressed(KEY_DOWN) || (IsKeyDown(KEY_DOWN) && autoCursorShouldTrigger)))
+            {
+                // Find start of current line
+                int currentLineStart = textBoxCursorIndex;
+                while (currentLineStart > 0 && text[currentLineStart - 1] != '\n') currentLineStart--;
+
+                // Calculate horizontal position in current line
+                int horizontalPos = textBoxCursorIndex - currentLineStart;
+
+                // Find end of current line
+                int currentLineEnd = textBoxCursorIndex;
+                while (currentLineEnd < textLength && text[currentLineEnd] != '\n') currentLineEnd++;
+
+                // Find next line
+                if (currentLineEnd < textLength)
+                {
+                    int nextLineStart = currentLineEnd + 1; // Skip the newline
+                    int nextLineEnd = nextLineStart;
+                    while (nextLineEnd < textLength && text[nextLineEnd] != '\n') nextLineEnd++;
+
+                    // Move to same horizontal position on next line (or end of line if shorter)
+                    int nextLineLength = nextLineEnd - nextLineStart;
+                    int targetPos = (horizontalPos < nextLineLength) ? horizontalPos : nextLineLength;
+                    textBoxCursorIndex = nextLineStart + targetPos;
+                }
+                else
+                {
+                    // Already on last line, move to end
+                    textBoxCursorIndex = textLength;
+                }
+            }
+
+            // Move cursor position with mouse
+            if (CheckCollisionPointRec(mousePosition, textBounds))     // Mouse hover text
+            {
+                float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize;
+                int codepointIndex = 0;
+                float glyphWidth = 0.0f;
+                float widthToMouseX = 0;
+                int mouseCursorIndex = 0;
+
+                for (int i = textIndexOffset; i < textLength; i += codepointSize)
+                {
+                    codepoint = GetCodepointNext(&text[i], &codepointSize);
+                    codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                    if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                    else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                    if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2)))
+                    {
+                        mouseCursor.x = textBounds.x + widthToMouseX;
+                        mouseCursorIndex = i;
+                        break;
+                    }
+
+                    widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                }
+
+                // Check if mouse cursor is at the last position
+                int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
+                if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
+                {
+                    mouseCursor.x = textBounds.x + textEndWidth;
+                    mouseCursorIndex = textLength;
+                }
+
+                // Place cursor at required index on mouse click
+                if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    cursor.x = mouseCursor.x;
+                    textBoxCursorIndex = mouseCursorIndex;
+                }
+            }
+            else mouseCursor.x = -1;
+
+            // Recalculate cursor position for multiline
+            if (multiline)
+            {
+                // Recalculate cursor line and position
+                int newCursorLine = 0;
+                int newLineStart = 0;
+
+                for (int i = 0; i < textBoxCursorIndex; i++)
+                {
+                    if (text[i] == '\n')
+                    {
+                        newCursorLine++;
+                        newLineStart = i + 1;
+                    }
+                }
+
+                // Extract current line text up to cursor
+                char currentLineText[1024] = { 0 };
+                int currentLineLen = 0;
+                int i = newLineStart;
+                while (i < textBoxCursorIndex && text[i] != '\n' && currentLineLen < 1023)
+                {
+                    currentLineText[currentLineLen++] = text[i++];
+                }
+                currentLineText[currentLineLen] = '\0';
+
+                cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(currentLineText) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+                cursor.y = textBounds.y + (newCursorLine * lineHeight * 1.5);
+            }
+            else
+            {
+                cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+            }
+
+            // Finish text editing on ENTER or mouse click outside bounds
+            if ((!multiline && IsKeyPressed(KEY_ENTER)) ||
+                (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                textBoxCursorIndex = 0;     // GLOBAL: Reset the shared cursor index
+                autoCursorCounter = 0;      // GLOBAL: Reset counter for repeated keystrokes
+                result = 1;
+            }
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePosition, bounds))
+            {
+                state = STATE_FOCUSED;
+
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
+                    autoCursorCounter = 0;             // GLOBAL: Reset counter for repeated keystrokes
+                    result = 1;
+                }
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_PRESSED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)));
+    }
+    else if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)));
+    }
+    else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK);
+
+    // Draw text considering index offset if required
+    // NOTE: Text index offset depends on cursor position
+    // Set vertical alignment to top for multiline
+    int prevVerticalAlignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
+    if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
+
+    GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))));
+
+    // Restore previous vertical alignment
+    if (multiline) GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, prevVerticalAlignment);
+
+    // Draw cursor
+    if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY))
+    {
+        //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0))
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+
+        // Draw mouse position cursor (if required)
+        if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+    }
+    else if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;      // Mouse button pressed: result = 1
+}
+
+/*
+// Text Box control with multiple lines and word-wrap
+// NOTE: This text-box is readonly, no editing supported by default
+bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    bool pressed = false;
+
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD);   // WARNING: If wrap mode enabled, text editing is not supported
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
+
+    // TODO: Implement methods to calculate cursor position properly
+    pressed = GuiTextBox(bounds, text, textSize, editMode);
+
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE);
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+
+    return pressed;
+}
+*/
+
+// Spinner control, returns selected value
+int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    int result = 1;
+    GuiState state = guiState;
+
+    int tempValue = *value;
+
+    Rectangle valueBoxBounds = {
+        bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+        bounds.y,
+        bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+        (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check spinner state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+
+#if defined(RAYGUI_NO_ICONS)
+    if (GuiButton(leftButtonBound, "<")) tempValue--;
+    if (GuiButton(rightButtonBound, ">")) tempValue++;
+#else
+    if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--;
+    if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++;
+#endif
+
+    if (!editMode)
+    {
+        if (tempValue < minValue) tempValue = minValue;
+        if (tempValue > maxValue) tempValue = maxValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
+
+    // Draw value selector custom buttons
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    *value = tempValue;
+    return result;
+}
+
+// Value Box control, updates input text with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+    snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Add new digit to text value
+            if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+            {
+                int key = GetCharPressed();
+
+                // Only allow keys in range [48..57]
+                if ((key >= 48) && (key <= 57))
+                {
+                    textValue[keyCount] = (char)key;
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Delete text
+            if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
+            {
+                keyCount--;
+                textValue[keyCount] = '\0';
+                valueHasChanged = true;
+            }
+
+            if (valueHasChanged) *value = TextToInteger(textValue);
+
+            // NOTE: We are not clamp values until user input finishes
+            //if (*value > maxValue) *value = maxValue;
+            //else if (*value < minValue) *value = minValue;
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                if (*value > maxValue) *value = maxValue;
+                else if (*value < minValue) *value = minValue;
+
+                result = 1;
+            }
+        }
+        else
+        {
+            if (*value > maxValue) *value = maxValue;
+            else if (*value < minValue) *value = minValue;
+
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor rectangle
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+            bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+            2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+        if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Floating point Value Box control, updates input val_str with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+    //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Only allow keys in range [48..57]
+            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            {
+                if (GuiGetTextWidth(textValue) < bounds.width)
+                {
+                    int key = GetCharPressed();
+                    if (((key >= 48) && (key <= 57)) ||
+                        (key == '.') ||
+                        ((keyCount == 0) && (key == '+')) ||  // NOTE: Sign can only be in first position
+                        ((keyCount == 0) && (key == '-')))
+                    {
+                        textValue[keyCount] = (char)key;
+                        keyCount++;
+
+                        valueHasChanged = true;
+                    }
+                }
+            }
+
+            // Pressed backspace
+            if (IsKeyPressed(KEY_BACKSPACE))
+            {
+                if (keyCount > 0)
+                {
+                    keyCount--;
+                    textValue[keyCount] = '\0';
+                    valueHasChanged = true;
+                }
+            }
+
+            if (valueHasChanged) *value = TextToFloat(textValue);
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+                            bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
+                            bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds,
+                (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT,
+                GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider control with pro parameters
+// NOTE: Other GuiSlider*() controls use this one
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+    float oldValue = *value;
+
+    int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
+    Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+                         0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if (*value > maxValue) *value = maxValue;
+        else if (*value < minValue) *value = minValue;
+    }
+
+    // Control value change check
+    if (oldValue == *value) result = 0;
+    else result = 1;
+
+    // Slider bar limits check
+    float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
+    if (sliderWidth > 0)        // Slider
+    {
+        slider.x += sliderValue;
+        slider.width = (float)sliderWidth;
+        if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
+        else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    else if (sliderWidth == 0)  // SliderBar
+    {
+        slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+        slider.width = sliderValue;
+        if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)?  BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+
+    // Draw slider internal bar (depends on state)
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+    else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider Bar control extended, returns selected value
+int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+    result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+    return result;
+}
+
+// Progress Bar control extended, shows current progress value
+int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+
+    // Progress bar
+    Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
+                           bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
+                           bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if (*value > maxValue) *value = maxValue;
+
+    // WARNING: Working with floats could lead to rounding issues
+    if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK);
+    }
+    else
+    {
+        if (*value > minValue)
+        {
+            // Draw progress bar with colored border, more visual
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        }
+        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+
+        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        else
+        {
+            // Draw borders not yet reached by value
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+        }
+
+        // Draw slider internal progress bar (depends on state)
+        GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+    }
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Status Bar control
+int GuiStatusBar(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dummy rectangle control, intended for placeholding
+int GuiDummyRec(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+    GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)));
+    //------------------------------------------------------------------
+
+    return result;
+}
+
+// List View control
+int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active)
+{
+    int result = 0;
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL);
+
+    return result;
+}
+
+// List View control with extended parameters
+int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int itemFocused = (focus == NULL)? -1 : *focus;
+    int itemSelected = (active == NULL)? -1 : *active;
+
+    // Check if we need a scroll bar
+    bool useScrollBar = false;
+    if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true;
+
+    // Define base item rectangle [0]
+    Rectangle itemBounds = { 0 };
+    itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING);
+    itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
+    if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
+
+    // Get items on the list
+    int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    if (visibleItems > count) visibleItems = count;
+
+    int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
+    if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
+    int endIndex = startIndex + visibleItems;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check mouse inside list view
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Check focused and selected item
+            for (int i = 0; i < visibleItems; i++)
+            {
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = startIndex + i;
+                    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                    {
+                        if (itemSelected == (startIndex + i)) itemSelected = -1;
+                        else itemSelected = startIndex + i;
+                    }
+                    break;
+                }
+
+                // Update item rectangle y position for next item
+                itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+            }
+
+            if (useScrollBar)
+            {
+                int wheelMove = (int)GetMouseWheelMove();
+                startIndex -= wheelMove;
+
+                if (startIndex < 0) startIndex = 0;
+                else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
+
+                endIndex = startIndex + visibleItems;
+                if (endIndex > count) endIndex = count;
+            }
+        }
+        else itemFocused = -1;
+
+        // Reset item rectangle y to [0]
+        itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+
+    // Draw visible items
+    for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
+    {
+        if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+
+        if (state == STATE_DISABLED)
+        {
+            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+
+            GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
+        }
+        else
+        {
+            if (((startIndex + i) == itemSelected) && (active != NULL))
+            {
+                // Draw item selected
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
+            }
+            else if (((startIndex + i) == itemFocused)) // && (focus != NULL))  // NOTE: We want items focused, despite not returned!
+            {
+                // Draw item focused
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
+            }
+            else
+            {
+                // Draw item normal (no rectangle)
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
+            }
+        }
+
+        // Update item rectangle y position for next item
+        itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    }
+
+    if (useScrollBar)
+    {
+        Rectangle scrollBarBounds = {
+            bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+        };
+
+        // Calculate percentage of visible items and apply same percentage to scrollbar
+        float percentVisible = (float)(endIndex - startIndex)/count;
+        float sliderSize = bounds.height*percentVisible;
+
+        int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);   // Save default slider size
+        int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize);            // Change slider size
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
+
+        startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
+
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default
+    }
+    //--------------------------------------------------------------------
+
+    if (active != NULL) *active = itemSelected;
+    if (focus != NULL) *focus = itemFocused;
+    if (scrollIndex != NULL) *scrollIndex = startIndex;
+
+    return result;
+}
+
+// Color Panel control - Color (RGBA) variant
+int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
+    Vector3 hsv = ConvertRGBtoHSV(vcolor);
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
+
+    GuiColorPanelHSV(bounds, text, &hsv);
+
+    // Check if the hsv was changed, only then change the color
+    // This is required, because the Color->HSV->Color conversion has precision errors
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+    // Otherwise GuiColorPanel would often modify it's color without user input
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
+    if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
+    {
+        Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+        // NOTE: Vector3ToColor() only available on raylib 1.8.1
+        *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+                            (unsigned char)(255.0f*rgb.y),
+                            (unsigned char)(255.0f*rgb.z),
+                            color->a };
+    }
+    return result;
+}
+
+// Color Bar Alpha control
+// NOTE: Returns alpha value normalized [0..1]
+int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+{
+    #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE)
+        #define RAYGUI_COLORBARALPHA_CHECKED_SIZE   10
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+        (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+        (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+        (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                    if (*alpha <= 0.0f) *alpha = 0.0f;
+                    if (*alpha >= 1.0f) *alpha = 1.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                if (*alpha <= 0.0f) *alpha = 0.0f;
+                if (*alpha >= 1.0f) *alpha = 1.0f;
+                //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw alpha bar: checked background
+    if (state != STATE_DISABLED)
+    {
+        int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+        int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+
+        for (int x = 0; x < checksX; x++)
+        {
+            for (int y = 0; y < checksY; y++)
+            {
+                Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE };
+                GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f));
+            }
+        }
+
+        DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw alpha bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Bar Hue control
+// Returns hue value normalized [0..1]
+// NOTE: Other similar bars (for reference):
+//      Color GuiColorBarSat() [WHITE->color]
+//      Color GuiColorBarValue() [BLACK->color], HSV/HSL
+//      float GuiColorBarLuminance() [BLACK->WHITE]
+int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                    if (*hue <= 0.0f) *hue = 0.0f;
+                    if (*hue >= 359.0f) *hue = 359.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                if (*hue <= 0.0f) *hue = 0.0f;
+                if (*hue >= 359.0f) *hue = 359.0f;
+
+            }
+            else state = STATE_FOCUSED;
+
+            /*if (IsKeyDown(KEY_UP))
+            {
+                hue -= 2.0f;
+                if (hue <= 0.0f) hue = 0.0f;
+            }
+            else if (IsKeyDown(KEY_DOWN))
+            {
+                hue += 2.0f;
+                if (hue >= 360.0f) hue = 360.0f;
+            }*/
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        // Draw hue bar:color bars
+        // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw hue bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Picker control
+// NOTE: It's divided in multiple controls:
+//      Color GuiColorPanel(Rectangle bounds, Color color)
+//      float GuiColorBarAlpha(Rectangle bounds, float alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanel() size
+// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead
+int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Color temp = { 200, 0, 0, 255 };
+    if (color == NULL) color = &temp;
+
+    GuiColorPanel(bounds, NULL, color);
+
+    Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+    //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
+
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
+    Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
+
+    GuiColorBarHue(boundsHue, NULL, &hsv.x);
+
+    //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
+    Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+    *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a };
+
+    return result;
+}
+
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
+// NOTE: It's divided in multiple controls:
+//      int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+//      int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanelHSV() size
+int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+
+    Vector3 tempHsv = { 0 };
+
+    if (colorHsv == NULL)
+    {
+        const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f };
+        tempHsv = ConvertRGBtoHSV(tempColor);
+        colorHsv = &tempHsv;
+    }
+
+    GuiColorPanelHSV(bounds, NULL, colorHsv);
+
+    const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+
+    GuiColorBarHue(boundsHue, NULL, &colorHsv->x);
+
+    return result;
+}
+
+// Color Panel control - HSV variant
+int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Vector2 pickerSelector = { 0 };
+
+    const Color colWhite = { 255, 255, 255, 255 };
+    const Color colBlack = { 0, 0, 0, 255 };
+
+    pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width;            // HSV: Saturation
+    pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height;  // HSV: Value
+
+    Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f };
+    Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
+    Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
+                      (unsigned char)(255.0f*rgbHue.y),
+                      (unsigned char)(255.0f*rgbHue.z), 255 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    pickerSelector = mousePoint;
+
+                    if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x;
+                    if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width;
+                    if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y;
+                    if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height;
+
+                    // Calculate color from picker
+                    Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                    colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                    colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                    colorHsv->y = colorPick.x;
+                    colorHsv->z = 1.0f - colorPick.y;
+
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds;
+                pickerSelector = mousePoint;
+
+                // Calculate color from picker
+                Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                colorHsv->y = colorPick.x;
+                colorHsv->z = 1.0f - colorPick.y;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
+        DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
+
+        // Draw color picker: selector
+        Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) };
+        GuiDrawRectangle(selector, 0, BLANK, colWhite);
+    }
+    else
+    {
+        DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
+    }
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Message Box control
+int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons)
+{
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT)
+        #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT    24
+    #endif
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING)
+        #define RAYGUI_MESSAGEBOX_BUTTON_PADDING   12
+    #endif
+
+    int result = -1;    // Returns clicked button from buttons list, 0 refers to closed window button
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    //int textWidth = GuiGetTextWidth(message) + 2;
+
+    Rectangle textBounds = { 0 };
+    textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2;
+    textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiLabel(textBounds, message);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+
+    prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Text Input Box control, ask for text
+int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive)
+{
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT      24
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING     12
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_HEIGHT             26
+    #endif
+
+    // Used to enable text edit mode
+    // WARNING: No more than one GuiTextInputBox() should be open at the same time
+    static bool textEditMode = false;
+
+    int result = -1;
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT;
+
+    int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+
+    Rectangle textBounds = { 0 };
+    if (message != NULL)
+    {
+        int textSize = GuiGetTextWidth(message) + 2;
+
+        textBounds.x = bounds.x + bounds.width/2 - textSize/2;
+        textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        textBounds.width = (float)textSize;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+    }
+
+    Rectangle textBoxBounds = { 0 };
+    textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2;
+    if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4);
+    textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2;
+    textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    // Draw message if available
+    if (message != NULL)
+    {
+        int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(textBounds, message);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+    }
+
+    int prevTextBoxAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT);
+    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+
+    if (secretViewActive != NULL)
+    {
+        static char stars[] = "****************";
+        if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height },
+            ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+
+        GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive);
+    }
+    else
+    {
+        if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+    }
+    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, prevTextBoxAlignment);
+
+    int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    if (result >= 0) textEditMode = false;
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Result is the pressed button index
+}
+
+// Grid control
+// NOTE: Returns grid mouse-hover selected cell
+// About drawing lines at subpixel spacing, simple put, not easy solution:
+// Ref: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
+int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell)
+{
+    // Grid lines alpha amount
+    #if !defined(RAYGUI_GRID_ALPHA)
+        #define RAYGUI_GRID_ALPHA    0.15f
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Vector2 mousePoint = GetMousePosition();
+    Vector2 currentMouseCell = { -1, -1 };
+
+    float spaceWidth = spacing/(float)subdivs;
+    int linesV = (int)(bounds.width/spaceWidth) + 1;
+    int linesH = (int)(bounds.height/spaceWidth) + 1;
+
+    int color = GuiGetStyle(DEFAULT, LINE_COLOR);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            // NOTE: Cell values must be the upper left of the cell the mouse is in
+            currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing);
+            currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing);
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED);
+
+    if (subdivs > 0)
+    {
+        // Draw vertical grid lines
+        for (int i = 0; i < linesV; i++)
+        {
+            Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 };
+            GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+
+        // Draw horizontal grid lines
+        for (int i = 0; i < linesH; i++)
+        {
+            Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 };
+            GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+    }
+
+    if (mouseCell != NULL) *mouseCell = currentMouseCell;
+    return result;
+}
+
+//----------------------------------------------------------------------------------
+// Tooltip management functions
+// NOTE: Tooltips requires some global variables: tooltipPtr
+//----------------------------------------------------------------------------------
+// Enable gui tooltips (global state)
+void GuiEnableTooltip(void) { guiTooltip = true; }
+
+// Disable gui tooltips (global state)
+void GuiDisableTooltip(void) { guiTooltip = false; }
+
+// Set tooltip string
+void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
+
+//----------------------------------------------------------------------------------
+// Styles loading functions
+//----------------------------------------------------------------------------------
+
+// Load raygui style file (.rgs)
+// NOTE: By default a binary file is expected, that file could contain a custom font,
+// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE)
+void GuiLoadStyle(const char *fileName)
+{
+    #define MAX_LINE_BUFFER_SIZE    256
+
+    bool tryBinary = false;
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+    // Try reading the files as text file first
+    FILE *rgsFile = fopen(fileName, "rt");
+
+    if (rgsFile != NULL)
+    {
+        char buffer[MAX_LINE_BUFFER_SIZE] = { 0 };
+        fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+
+        if (buffer[0] == '#')
+        {
+            int controlId = 0;
+            int propertyId = 0;
+            unsigned int propertyValue = 0;
+
+            while (!feof(rgsFile))
+            {
+                switch (buffer[0])
+                {
+                    case 'p':
+                    {
+                        // Style property: p <control_id> <property_id> <property_value> <property_name>
+
+                        sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue);
+                        GuiSetStyle(controlId, propertyId, (int)propertyValue);
+
+                    } break;
+                    case 'f':
+                    {
+                        // Style font: f <gen_font_size> <charmap_file> <font_file>
+
+                        int fontSize = 0;
+                        char charmapFileName[256] = { 0 };
+                        char fontFileName[256] = { 0 };
+                        sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName);
+
+                        Font font = { 0 };
+                        int *codepoints = NULL;
+                        int codepointCount = 0;
+
+                        if (charmapFileName[0] != '0')
+                        {
+                            // Load text data from file
+                            // NOTE: Expected an UTF-8 array of codepoints, no separation
+                            char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName));
+                            codepoints = LoadCodepoints(textData, &codepointCount);
+                            UnloadFileText(textData);
+                        }
+
+                        if (fontFileName[0] != '\0')
+                        {
+                            // In case a font is already loaded and it is not default internal font, unload it
+                            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+
+                            if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount);
+                            else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0);   // Default to 95 standard codepoints
+                        }
+
+                        // If font texture not properly loaded, revert to default font and size/spacing
+                        if (font.texture.id == 0)
+                        {
+                            font = GetFontDefault();
+                            GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+                            GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+                        }
+
+                        UnloadCodepoints(codepoints);
+
+                        if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font);
+
+                    } break;
+                    default: break;
+                }
+
+                fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+            }
+        }
+        else tryBinary = true;
+
+        fclose(rgsFile);
+    }
+
+    if (tryBinary)
+    {
+        rgsFile = fopen(fileName, "rb");
+
+        if (rgsFile != NULL)
+        {
+            fseek(rgsFile, 0, SEEK_END);
+            int fileDataSize = ftell(rgsFile);
+            fseek(rgsFile, 0, SEEK_SET);
+
+            if (fileDataSize > 0)
+            {
+                unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
+                fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
+
+                GuiLoadStyleFromMemory(fileData, fileDataSize);
+
+                RAYGUI_FREE(fileData);
+            }
+
+            fclose(rgsFile);
+        }
+    }
+}
+
+// Load style default over global style
+void GuiLoadStyleDefault(void)
+{
+    // We set this variable first to avoid cyclic function calls
+    // when calling GuiSetStyle() and GuiGetStyle()
+    guiStyleLoaded = true;
+
+    // Initialize default LIGHT style property values
+    // WARNING: Default value are applied to all controls on set but
+    // they can be overwritten later on for every custom control
+    GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff);
+    GuiSetStyle(DEFAULT, BORDER_WIDTH, 1);
+    GuiSetStyle(DEFAULT, TEXT_PADDING, 0);
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    // Initialize default extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(DEFAULT, TEXT_SIZE, 10);                // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, TEXT_SPACING, 1);              // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff);       // DEFAULT specific property
+    GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property
+    GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15);        // DEFAULT, 15 pixels between lines
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);   // DEFAULT, text aligned vertically to middle of text-bounds
+
+    // Initialize control-specific property values
+    // NOTE: Those properties are in default list but require specific values by control type
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
+    GuiSetStyle(SLIDER, TEXT_PADDING, 4);
+    GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT);
+    GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0);
+    GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiSetStyle(TEXTBOX, TEXT_PADDING, 4);
+    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
+    GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
+    GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+
+    // Initialize extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(TOGGLE, GROUP_PADDING, 2);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 16);
+    GuiSetStyle(SLIDER, SLIDER_PADDING, 1);
+    GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1);
+    GuiSetStyle(CHECKBOX, CHECK_PADDING, 1);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
+    GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
+    GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
+    GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16);
+    GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
+    GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16);
+    GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2);
+
+    if (guiFont.texture.id != GetFontDefault().texture.id)
+    {
+        // Unload previous font texture
+        UnloadTexture(guiFont.texture);
+        RAYGUI_FREE(guiFont.recs);
+        RAYGUI_FREE(guiFont.glyphs);
+        guiFont.recs = NULL;
+        guiFont.glyphs = NULL;
+
+        // Setup default raylib font
+        guiFont = GetFontDefault();
+
+        // NOTE: Default raylib font character 95 is a white square
+        Rectangle whiteChar = guiFont.recs[95];
+
+        // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
+        SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 });
+    }
+}
+
+// Get text with icon id prepended
+// NOTE: Useful to add icons by name id (enum) instead of
+// a number that can change between ricon versions
+const char *GuiIconText(int iconId, const char *text)
+{
+#if defined(RAYGUI_NO_ICONS)
+    return NULL;
+#else
+    static char buffer[1024] = { 0 };
+    static char iconBuffer[16] = { 0 };
+
+    if (text != NULL)
+    {
+        memset(buffer, 0, 1024);
+        snprintf(buffer, 1024, "#%03i#", iconId);
+
+        for (int i = 5; i < 1024; i++)
+        {
+            buffer[i] = text[i - 5];
+            if (text[i - 5] == '\0') break;
+        }
+
+        return buffer;
+    }
+    else
+    {
+        snprintf(iconBuffer, 16, "#%03i#", iconId);
+
+        return iconBuffer;
+    }
+#endif
+}
+
+#if !defined(RAYGUI_NO_ICONS)
+// Get full icons data pointer
+unsigned int *GuiGetIcons(void) { return guiIconsPtr; }
+
+// Load raygui icons file (.rgi)
+// NOTE: In case nameIds are required, they can be requested with loadIconsName,
+// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH],
+// WARNING: guiIconsName[]][] memory should be manually freed!
+char **GuiLoadIcons(const char *fileName, bool loadIconsName)
+{
+    // Style File Structure (.rgi)
+    // ------------------------------------------------------
+    // Offset  | Size    | Type       | Description
+    // ------------------------------------------------------
+    // 0       | 4       | char       | Signature: "rGI "
+    // 4       | 2       | short      | Version: 100
+    // 6       | 2       | short      | reserved
+
+    // 8       | 2       | short      | Num icons (N)
+    // 10      | 2       | short      | Icons size (Options: 16, 32, 64) (S)
+
+    // Icons name id (32 bytes per name id)
+    // foreach (icon)
+    // {
+    //   12+32*i  | 32   | char       | Icon NameId
+    // }
+
+    // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size)
+    // S*S pixels/32bit per unsigned int = K unsigned int per icon
+    // foreach (icon)
+    // {
+    //   ...   | K       | unsigned int | Icon Data
+    // }
+
+    FILE *rgiFile = fopen(fileName, "rb");
+
+    char **guiIconsName = NULL;
+
+    if (rgiFile != NULL)
+    {
+        char signature[5] = { 0 };
+        short version = 0;
+        short reserved = 0;
+        short iconCount = 0;
+        short iconSize = 0;
+
+        fread(signature, 1, 4, rgiFile);
+        fread(&version, sizeof(short), 1, rgiFile);
+        fread(&reserved, sizeof(short), 1, rgiFile);
+        fread(&iconCount, sizeof(short), 1, rgiFile);
+        fread(&iconSize, sizeof(short), 1, rgiFile);
+
+        if ((signature[0] == 'r') &&
+            (signature[1] == 'G') &&
+            (signature[2] == 'I') &&
+            (signature[3] == ' '))
+        {
+            if (loadIconsName)
+            {
+                guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+                for (int i = 0; i < iconCount; i++)
+                {
+                    guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                    fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
+                }
+            }
+            else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
+
+            // Read icons data directly over internal icons array
+            fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
+        }
+
+        fclose(rgiFile);
+    }
+
+    return guiIconsName;
+}
+
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+    char **guiIconsName = NULL;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    short iconCount = 0;
+    short iconSize = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+    memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'I') &&
+        (signature[3] == ' '))
+    {
+        if (loadIconsName)
+        {
+            guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+            for (int i = 0; i < iconCount; i++)
+            {
+                guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+                fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+            }
+        }
+        else
+        {
+            // Skip icon name data if not required
+            fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+        }
+
+        int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+        guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+        memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+    }
+
+    return guiIconsName;
+}
+
+// Draw selected icon using rectangles pixel-by-pixel
+void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
+{
+    #define BIT_CHECK(a,b) ((a) & (1u<<(b)))
+
+    for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++)
+    {
+        for (int k = 0; k < 32; k++)
+        {
+            if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k))
+            {
+            #if !defined(RAYGUI_STANDALONE)
+                GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color);
+            #endif
+            }
+
+            if ((k == 15) || (k == 31)) y++;
+        }
+    }
+}
+
+// Set icon drawing size
+void GuiSetIconScale(int scale)
+{
+    if (scale >= 1) guiIconScale = scale;
+}
+
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    Vector2 textSize = { 0 };
+    int textIconOffset = 0;
+
+    if ((text != NULL) && (text[0] != '\0'))
+    {
+        if (text[0] == '#')
+        {
+            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+            {
+                if (text[i] == '#')
+                {
+                    textIconOffset = i;
+                    break;
+                }
+            }
+        }
+
+        text += textIconOffset;
+
+        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+        // Custom MeasureText() implementation
+        if ((guiFont.texture.id > 0) && (text != NULL))
+        {
+            // Get size in bytes of text, considering end of line and line break
+            int size = 0;
+            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+            {
+                if ((text[i] != '\0') && (text[i] != '\n')) size++;
+                else break;
+            }
+
+            float scaleFactor = fontSize/(float)guiFont.baseSize;
+            textSize.y = (float)guiFont.baseSize*scaleFactor;
+            float glyphWidth = 0.0f;
+
+            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+            {
+                int codepoint = GetCodepointNext(&text[i], &codepointSize);
+                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+    }
+
+    return (int)textSize.x;
+}
+
+#endif      // !RAYGUI_NO_ICONS
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Load style from memory
+// WARNING: Binary files only
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    int propertyCount = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'S') &&
+        (signature[3] == ' '))
+    {
+        short controlId = 0;
+        short propertyId = 0;
+        unsigned int propertyValue = 0;
+
+        for (int i = 0; i < propertyCount; i++)
+        {
+            memcpy(&controlId, fileDataPtr, sizeof(short));
+            memcpy(&propertyId, fileDataPtr + 2, sizeof(short));
+            memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int));
+            fileDataPtr += 8;
+
+            if (controlId == 0) // DEFAULT control
+            {
+                // If a DEFAULT property is loaded, it is propagated to all controls
+                // NOTE: All DEFAULT properties should be defined first in the file
+                GuiSetStyle(0, (int)propertyId, propertyValue);
+
+                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue);
+            }
+            else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
+        }
+
+        // Font loading is highly dependant on raylib API to load font data and image
+
+#if !defined(RAYGUI_STANDALONE)
+        // Load custom font if available
+        int fontDataSize = 0;
+        memcpy(&fontDataSize, fileDataPtr, sizeof(int));
+        fileDataPtr += 4;
+
+        if (fontDataSize > 0)
+        {
+            Font font = { 0 };
+            int fontType = 0;   // 0-Normal, 1-SDF
+
+            memcpy(&font.baseSize, fileDataPtr, sizeof(int));
+            memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int));
+            memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            // Load font white rectangle
+            Rectangle fontWhiteRec = { 0 };
+            memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle));
+            fileDataPtr += 16;
+
+            // Load font image parameters
+            int fontImageUncompSize = 0;
+            int fontImageCompSize = 0;
+            memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int));
+            memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int));
+            fileDataPtr += 8;
+
+            Image imFont = { 0 };
+            imFont.mipmaps = 1;
+            memcpy(&imFont.width, fileDataPtr, sizeof(int));
+            memcpy(&imFont.height, fileDataPtr + 4, sizeof(int));
+            memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize))
+            {
+                // Compressed font atlas image data (DEFLATE), it requires DecompressData()
+                int dataUncompSize = 0;
+                unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
+                memcpy(compData, fileDataPtr, fontImageCompSize);
+                fileDataPtr += fontImageCompSize;
+
+                imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
+
+                // Security check, dataUncompSize must match the provided fontImageUncompSize
+                if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
+
+                RAYGUI_FREE(compData);
+            }
+            else
+            {
+                // Font atlas image data is not compressed
+                imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
+                memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
+                fileDataPtr += fontImageUncompSize;
+            }
+
+            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+            font.texture = LoadTextureFromImage(imFont);
+
+            RAYGUI_FREE(imFont.data);
+
+            // Validate font atlas texture was loaded correctly
+            if (font.texture.id != 0)
+            {
+                // Load font recs data
+                int recsDataSize = font.glyphCount*sizeof(Rectangle);
+                int recsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed recs data
+                    memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
+                {
+                    // Recs data is compressed, uncompress it
+                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
+                    fileDataPtr += recsDataCompressedSize;
+
+                    int recsDataUncompSize = 0;
+                    font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted");
+
+                    RAYGUI_FREE(recsDataCompressed);
+                }
+                else
+                {
+                    // Recs data is uncompressed
+                    font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle));
+                        fileDataPtr += sizeof(Rectangle);
+                    }
+                }
+
+                // Load font glyphs info data
+                int glyphsDataSize = font.glyphCount*16;    // 16 bytes data per glyph
+                int glyphsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed glyphs data
+                    memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                // Allocate required glyphs space to fill with data
+                font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
+
+                if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
+                {
+                    // Glyphs data is compressed, uncompress it
+                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
+                    fileDataPtr += glyphsDataCompressedSize;
+
+                    int glyphsDataUncompSize = 0;
+                    unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted");
+
+                    unsigned char *glyphsDataUncompPtr = glyphsDataUncomp;
+
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int));
+                        glyphsDataUncompPtr += 16;
+                    }
+
+                    RAYGUI_FREE(glypsDataCompressed);
+                    RAYGUI_FREE(glyphsDataUncomp);
+                }
+                else
+                {
+                    // Glyphs data is uncompressed
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int));
+                        fileDataPtr += 16;
+                    }
+                }
+            }
+            else font = GetFontDefault();   // Fallback in case of errors loading font atlas texture
+
+            GuiSetFont(font);
+
+            // Set font texture source rectangle to be used as white texture to draw shapes
+            // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call
+            if ((fontWhiteRec.x > 0) &&
+                (fontWhiteRec.y > 0) &&
+                (fontWhiteRec.width > 0) &&
+                (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec);
+        }
+#endif
+    }
+}
+
+// Get text bounds considering control bounds
+static Rectangle GetTextBounds(int control, Rectangle bounds)
+{
+    Rectangle textBounds = bounds;
+
+    textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH);
+    textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING);
+    textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);
+    textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);    // NOTE: Text is processed line per line!
+
+    // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds
+    switch (control)
+    {
+        case COMBOBOX:
+        case DROPDOWNBOX:
+        case LISTVIEW:
+            // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW
+        case SLIDER:
+        case CHECKBOX:
+        case VALUEBOX:
+        case CONTROL11:
+            // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
+        default:
+        {
+            // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText()
+            if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING);
+            else textBounds.x += GuiGetStyle(control, TEXT_PADDING);
+        }
+        break;
+    }
+
+    return textBounds;
+}
+
+// Get text icon if provided and move text cursor
+// NOTE: We support up to 999 values for iconId
+static const char *GetTextIcon(const char *text, int *iconId)
+{
+#if !defined(RAYGUI_NO_ICONS)
+    *iconId = -1;
+    if (text[0] == '#')     // Maybe we have an icon!
+    {
+        char iconValue[4] = { 0 };  // Maximum length for icon value: 3 digits + '\0'
+
+        int pos = 1;
+        while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9'))
+        {
+            iconValue[pos - 1] = text[pos];
+            pos++;
+        }
+
+        if (text[pos] == '#')
+        {
+            *iconId = TextToInteger(iconValue);
+
+            // Move text pointer after icon
+            // WARNING: If only icon provided, it could point to EOL character: '\0'
+            if (*iconId >= 0) text += (pos + 1);
+        }
+    }
+#endif
+
+    return text;
+}
+
+// Get text divided into lines (by line-breaks '\n')
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
+{
+    #define RAYGUI_MAX_TEXT_LINES   128
+
+    static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+    for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL;    // Init NULL pointers to substrings
+
+    int textLength = (int)strlen(text);
+
+    lines[0] = text;
+    *count = 1;
+
+    for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
+    {
+        if (text[i] == '\n')
+        {
+            k++;
+            lines[k] = &text[i + 1]; // WARNING: next value is valid?
+            *count += 1;
+        }
+    }
+
+    return lines;
+}
+
+// Get text width to next space for provided string
+static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex)
+{
+    float width = 0;
+    int codepointByteCount = 0;
+    int codepoint = 0;
+    int index = 0;
+    float glyphWidth = 0;
+    float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+    for (int i = 0; text[i] != '\0'; i++)
+    {
+        if (text[i] != ' ')
+        {
+            codepoint = GetCodepoint(&text[i], &codepointByteCount);
+            index = GetGlyphIndex(guiFont, codepoint);
+            glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor;
+            width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+        }
+        else
+        {
+            *nextSpaceIndex = i;
+            break;
+        }
+    }
+
+    return width;
+}
+
+// Gui draw text using default font
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint)
+{
+    #define TEXT_VALIGN_PIXEL_OFFSET(h)  ((int)h%2)     // Vertical alignment for pixel perfect
+
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    if ((text == NULL) || (text[0] == '\0')) return;    // Security check
+
+    // PROCEDURE:
+    //   - Text is processed line per line
+    //   - For every line, horizontal alignment is defined
+    //   - For all text, vertical alignment is defined (multiline text only)
+    //   - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only)
+
+    // Get text lines (using '\n' as delimiter) to be processed individually
+    // WARNING: We can't use GuiTextSplit() function because it can be already used
+    // before the GuiDrawText() call and its buffer is static, it would be overriden :(
+    int lineCount = 0;
+    const char **lines = GetTextLines(text, &lineCount);
+
+    // Text style variables
+    //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
+    int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);    // Wrap-mode only available in read-only mode, no for text editing
+
+    // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap
+    float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2);
+    float posOffsetY = 0.0f;
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        int iconId = 0;
+        lines[i] = GetTextIcon(lines[i], &iconId);      // Check text for icon and move cursor
+
+        // Get text position depending on alignment and iconId
+        //---------------------------------------------------------------------------------
+        Vector2 textBoundsPosition = { textBounds.x, textBounds.y };
+        float textBoundsWidthOffset = 0.0f;
+
+        // NOTE: We get text size after icon has been processed
+        // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+        int textSizeX = GuiGetTextWidth(lines[i]);
+
+        // If text requires an icon, add size to measure
+        if (iconId >= 0)
+        {
+            textSizeX += RAYGUI_ICON_SIZE*guiIconScale;
+
+            // WARNING: If only icon provided, text could be pointing to EOF character: '\0'
+#if !defined(RAYGUI_NO_ICONS)
+            if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING;
+#endif
+        }
+
+        // Check guiTextAlign global variables
+        switch (alignment)
+        {
+            case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break;
+            case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x +  textBounds.width/2 - textSizeX/2; break;
+            case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break;
+            default: break;
+        }
+
+        if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x;
+
+        switch (alignmentVertical)
+        {
+            // Only valid in case of wordWrap = 0;
+            case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break;
+            case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            default: break;
+        }
+
+        // NOTE: Make sure we get pixel-perfect coordinates,
+        // In case of decimals we got weird text positioning
+        textBoundsPosition.x = (float)((int)textBoundsPosition.x);
+        textBoundsPosition.y = (float)((int)textBoundsPosition.y);
+        //---------------------------------------------------------------------------------
+
+        // Draw text (with icon if available)
+        //---------------------------------------------------------------------------------
+#if !defined(RAYGUI_NO_ICONS)
+        if (iconId >= 0)
+        {
+            // NOTE: We consider icon height, probably different than text size
+            GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint);
+            textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+            textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+        }
+#endif
+        // Get size in bytes of text,
+        // considering end of line and line break
+        int lineSize = 0;
+        for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ }
+        float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+        int lastSpaceIndex = 0;
+        bool tempWrapCharMode = false;
+
+        int textOffsetY = 0;
+        float textOffsetX = 0.0f;
+        float glyphWidth = 0;
+
+        int ellipsisWidth = GuiGetTextWidth("...");
+        bool textOverflow = false;
+        for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
+        {
+            int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
+            int index = GetGlyphIndex(guiFont, codepoint);
+
+            // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+            // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+            if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size
+
+            // Get glyph width to check if it goes out of bounds
+            if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor);
+            else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor;
+
+            // Wrap mode text measuring, to validate if
+            // it can be drawn or a new line is required
+            if (wrapMode == TEXT_WRAP_CHAR)
+            {
+                // Jump to next line if current character reach end of the box limits
+                if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+
+                    if (tempWrapCharMode)   // Wrap at char level when too long words
+                    {
+                        wrapMode = TEXT_WRAP_WORD;
+                        tempWrapCharMode = false;
+                    }
+                }
+            }
+            else if (wrapMode == TEXT_WRAP_WORD)
+            {
+                if (codepoint == 32) lastSpaceIndex = c;
+
+                // Get width to next space in line
+                int nextSpaceIndex = 0;
+                float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex);
+
+                int nextSpaceIndex2 = 0;
+                float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2);
+
+                if (nextWordSize > textBounds.width - textBoundsWidthOffset)
+                {
+                    // Considering the case the next word is longer than bounds
+                    tempWrapCharMode = true;
+                    wrapMode = TEXT_WRAP_CHAR;
+                }
+                else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+                }
+            }
+
+            if (codepoint == '\n') break;   // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint
+            else
+            {
+                // TODO: There are multiple types of spaces in Unicode,
+                // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html
+                if ((codepoint != ' ') && (codepoint != '\t'))      // Do not draw codepoints with no glyph
+                {
+                    if (wrapMode == TEXT_WRAP_NONE)
+                    {
+                        // Draw only required text glyphs fitting the textBounds.width
+                        if (textSizeX > textBounds.width)
+                        {
+                            if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth))
+                            {
+                                DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                            }
+                            else if (!textOverflow)
+                            {
+                                textOverflow = true;
+
+                                for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3)
+                                {
+                                    DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                                }
+                            }
+                        }
+                        else
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                    else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD))
+                    {
+                        // Draw only glyphs inside the bounds
+                        if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE)))
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                }
+
+                if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+        else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING));
+        //---------------------------------------------------------------------------------
+    }
+
+#if defined(RAYGUI_DEBUG_TEXT_BOUNDS)
+    GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f));
+#endif
+}
+
+// Gui draw rectangle using default raygui plain style with borders
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color)
+{
+    if (color.a > 0)
+    {
+        // Draw rectangle filled with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha));
+    }
+
+    if (borderWidth > 0)
+    {
+        // Draw rectangle border lines with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+    }
+
+#if defined(RAYGUI_DEBUG_RECS_BOUNDS)
+    DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f));
+#endif
+}
+
+// Draw tooltip using control bounds
+static void GuiTooltip(Rectangle controlRec)
+{
+    if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode)
+    {
+        Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+
+        if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
+
+        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
+
+        int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
+        int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_PADDING, 0);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
+        GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
+    }
+}
+
+// Split controls text into multiple strings
+// Also check for multiple columns (required by GuiToggleGroup())
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+    // NOTE: Those definitions could be externally provided if required
+
+    // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user
+    // textRow is an externally provided array of integers that stores row number for every splitted string
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE     1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };   // String pointers array (points to buffer data)
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };         // Buffer data (text input copy with '\0' added)
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 1;
+
+    if (textRow != NULL) textRow[0] = 0;
+
+    // Count how many substrings we have on text and point to every one
+    for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+    {
+        buffer[i] = text[i];
+        if (buffer[i] == '\0') break;
+        else if ((buffer[i] == delimiter) || (buffer[i] == '\n'))
+        {
+            result[counter] = buffer + i + 1;
+
+            if (textRow != NULL)
+            {
+                if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1;
+                else textRow[counter] = textRow[counter - 1];
+            }
+
+            buffer[i] = '\0';   // Set an end of string at this point
+
+            counter++;
+            if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+        }
+    }
+
+    *count = counter;
+
+    return result;
+}
+
+// Convert color data from RGB to HSV
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertRGBtoHSV(Vector3 rgb)
+{
+    Vector3 hsv = { 0 };
+    float min = 0.0f;
+    float max = 0.0f;
+    float delta = 0.0f;
+
+    min = (rgb.x < rgb.y)? rgb.x : rgb.y;
+    min = (min < rgb.z)? min  : rgb.z;
+
+    max = (rgb.x > rgb.y)? rgb.x : rgb.y;
+    max = (max > rgb.z)? max  : rgb.z;
+
+    hsv.z = max;            // Value
+    delta = max - min;
+
+    if (delta < 0.00001f)
+    {
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    if (max > 0.0f)
+    {
+        // NOTE: If max is 0, this divide would cause a crash
+        hsv.y = (delta/max);    // Saturation
+    }
+    else
+    {
+        // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    // NOTE: Comparing float values could not work properly
+    if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta;    // Between yellow & magenta
+    else
+    {
+        if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta;  // Between cyan & yellow
+        else hsv.x = 4.0f + (rgb.x - rgb.y)/delta;      // Between magenta & cyan
+    }
+
+    hsv.x *= 60.0f;     // Convert to degrees
+
+    if (hsv.x < 0.0f) hsv.x += 360.0f;
+
+    return hsv;
+}
+
+// Convert color data from HSV to RGB
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertHSVtoRGB(Vector3 hsv)
+{
+    Vector3 rgb = { 0 };
+    float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f;
+    long i = 0;
+
+    // NOTE: Comparing float values could not work properly
+    if (hsv.y <= 0.0f)
+    {
+        rgb.x = hsv.z;
+        rgb.y = hsv.z;
+        rgb.z = hsv.z;
+        return rgb;
+    }
+
+    hh = hsv.x;
+    if (hh >= 360.0f) hh = 0.0f;
+    hh /= 60.0f;
+
+    i = (long)hh;
+    ff = hh - i;
+    p = hsv.z*(1.0f - hsv.y);
+    q = hsv.z*(1.0f - (hsv.y*ff));
+    t = hsv.z*(1.0f - (hsv.y*(1.0f - ff)));
+
+    switch (i)
+    {
+        case 0:
+        {
+            rgb.x = hsv.z;
+            rgb.y = t;
+            rgb.z = p;
+        } break;
+        case 1:
+        {
+            rgb.x = q;
+            rgb.y = hsv.z;
+            rgb.z = p;
+        } break;
+        case 2:
+        {
+            rgb.x = p;
+            rgb.y = hsv.z;
+            rgb.z = t;
+        } break;
+        case 3:
+        {
+            rgb.x = p;
+            rgb.y = q;
+            rgb.z = hsv.z;
+        } break;
+        case 4:
+        {
+            rgb.x = t;
+            rgb.y = p;
+            rgb.z = hsv.z;
+        } break;
+        case 5:
+        default:
+        {
+            rgb.x = hsv.z;
+            rgb.y = p;
+            rgb.z = q;
+        } break;
+    }
+
+    return rgb;
+}
+
+// Scroll bar control (used by GuiScrollPanel())
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
+{
+    GuiState state = guiState;
+
+    // Is the scrollbar horizontal or vertical?
+    bool isVertical = (bounds.width > bounds.height)? false : true;
+
+    // The size (width or height depending on scrollbar type) of the spinner buttons
+    const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)?
+        (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) :
+        (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0;
+
+    // Arrow buttons [<] [>] [∧] [∨]
+    Rectangle arrowUpLeft = { 0 };
+    Rectangle arrowDownRight = { 0 };
+
+    // Actual area of the scrollbar excluding the arrow buttons
+    Rectangle scrollbar = { 0 };
+
+    // Slider bar that moves     --[///]-----
+    Rectangle slider = { 0 };
+
+    // Normalize value
+    if (value > maxValue) value = maxValue;
+    if (value < minValue) value = minValue;
+
+    int valueRange = maxValue - minValue;
+    if (valueRange <= 0) valueRange = 1;
+
+    int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+    if (sliderSize < 1) sliderSize = 1;  // TODO: Consider a minimum slider size
+
+    // Calculate rectangles for all of the components
+    arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
+        (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)spinnerSize, (float)spinnerSize };
+
+    if (isVertical)
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)),
+            bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)),
+            (float)sliderSize };
+    }
+    else    // horizontal
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)),
+            bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            (float)sliderSize,
+            bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) };
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
+                !CheckCollisionPointRec(mousePoint, arrowUpLeft) &&
+                !CheckCollisionPointRec(mousePoint, arrowDownRight))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Handle mouse wheel
+            int wheel = (int)GetMouseWheelMove();
+            if (wheel != 0) value += wheel;
+
+            // Handle mouse button down
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                // Check arrows click
+                if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // If click on scrollbar position but not on slider, place slider directly on that position
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+
+                state = STATE_PRESSED;
+            }
+
+            // Keyboard control on mouse hover scrollbar
+            /*
+            if (isVertical)
+            {
+                if (IsKeyDown(KEY_DOWN)) value += 5;
+                else if (IsKeyDown(KEY_UP)) value -= 5;
+            }
+            else
+            {
+                if (IsKeyDown(KEY_RIGHT)) value += 5;
+                else if (IsKeyDown(KEY_LEFT)) value -= 5;
+            }
+            */
+        }
+
+        // Normalize value
+        if (value > maxValue) value = maxValue;
+        if (value < minValue) value = minValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)));   // Draw the background
+
+    GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)));     // Draw the scrollbar active area background
+    GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3)));         // Draw the slider bar
+
+    // Draw arrows (using icon if available)
+    if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE))
+    {
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText(isVertical? "^" : "<",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+        GuiDrawText(isVertical? "v" : ">",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(isVertical? "#121#" : "#118#",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL
+        GuiDrawText(isVertical? "#120#" : "#119#",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    return value;
+}
+
+// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+// WARNING: It multiplies current alpha by alpha scale factor
+static Color GuiFade(Color color, float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) };
+
+    return result;
+}
+
+#if defined(RAYGUI_STANDALONE)
+// Returns a Color struct from hexadecimal value
+static Color GetColor(int hexValue)
+{
+    Color color;
+
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
+
+    return color;
+}
+
+// Returns hexadecimal value for a Color
+static int ColorToInt(Color color)
+{
+    return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
+}
+
+// Check if point is inside rectangle
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
+{
+    bool collision = false;
+
+    if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) &&
+        (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
+
+    return collision;
+}
+
+// Formatting of text with variables to 'embed'
+static const char *TextFormat(const char *text, ...)
+{
+    #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE)
+        #define RAYGUI_TEXTFORMAT_MAX_SIZE   256
+    #endif
+
+    static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE];
+
+    va_list args;
+    va_start(args, text);
+    vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
+    va_end(args);
+
+    return buffer;
+}
+
+// Draw rectangle with vertical gradient fill color
+// NOTE: This function is only used by GuiColorPicker()
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
+{
+    Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height };
+    DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+}
+
+// Split string into multiple strings
+const char **TextSplit(const char *text, char delimiter, int *count)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE      1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 0;
+
+    if (text != NULL)
+    {
+        counter = 1;
+
+        // Count how many substrings we have on text and point to every one
+        for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+        {
+            buffer[i] = text[i];
+            if (buffer[i] == '\0') break;
+            else if (buffer[i] == delimiter)
+            {
+                buffer[i] = '\0';   // Set an end of string at this point
+                result[counter] = buffer + i + 1;
+                counter++;
+
+                if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            }
+        }
+    }
+
+    *count = counter;
+    return result;
+}
+
+// Get integer value from text
+// NOTE: This function replaces atoi() [stdlib.h]
+static int TextToInteger(const char *text)
+{
+    int value = 0;
+    int sign = 1;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1;
+        text++;
+    }
+
+    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+
+    return value*sign;
+}
+
+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+    float value = 0.0f;
+    float sign = 1.0f;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1.0f;
+        text++;
+    }
+
+    int i = 0;
+    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+    if (text[i++] != '.') value *= sign;
+    else
+    {
+        float divisor = 10.0f;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        {
+            value += ((float)(text[i] - '0'))/divisor;
+            divisor = divisor*10.0f;
+        }
+    }
+
+    return value;
+}
+
+// Encode codepoint into UTF-8 text (char array size returned as parameter)
+static const char *CodepointToUTF8(int codepoint, int *byteSize)
+{
+    static char utf8[6] = { 0 };
+    int size = 0;
+
+    if (codepoint <= 0x7f)
+    {
+        utf8[0] = (char)codepoint;
+        size = 1;
+    }
+    else if (codepoint <= 0x7ff)
+    {
+        utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
+        utf8[1] = (char)((codepoint & 0x3f) | 0x80);
+        size = 2;
+    }
+    else if (codepoint <= 0xffff)
+    {
+        utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
+        utf8[1] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[2] = (char)((codepoint & 0x3f) | 0x80);
+        size = 3;
+    }
+    else if (codepoint <= 0x10ffff)
+    {
+        utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
+        utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
+        utf8[2] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[3] = (char)((codepoint & 0x3f) | 0x80);
+        size = 4;
+    }
+
+    *byteSize = size;
+
+    return utf8;
+}
+
+// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
+// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
+// Total number of bytes processed are returned as a parameter
+// NOTE: the standard says U+FFFD should be returned in case of errors
+// but that character is not supported by the default font in raylib
+static int GetCodepointNext(const char *text, int *codepointSize)
+{
+    const char *ptr = text;
+    int codepoint = 0x3f;       // Codepoint (defaults to '?')
+    *codepointSize = 1;
+
+    // Get current codepoint and bytes processed
+    if (0xf0 == (0xf8 & ptr[0]))
+    {
+        // 4 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
+        *codepointSize = 4;
+    }
+    else if (0xe0 == (0xf0 & ptr[0]))
+    {
+        // 3 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
+        *codepointSize = 3;
+    }
+    else if (0xc0 == (0xe0 & ptr[0]))
+    {
+        // 2 byte UTF-8 codepoint
+        if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
+        *codepointSize = 2;
+    }
+    else if (0x00 == (0x80 & ptr[0]))
+    {
+        // 1 byte UTF-8 codepoint
+        codepoint = ptr[0];
+        *codepointSize = 1;
+    }
+
+    return codepoint;
+}
+#endif      // RAYGUI_STANDALONE
+
+#endif      // RAYGUI_IMPLEMENTATION
--- a/third_party/raylib/raylib.bzl	Tue Dec 16 21:01:45 2025 -0500
+++ b/third_party/raylib/raylib.bzl	Sat Dec 20 13:56:01 2025 -0500
@@ -18,7 +18,9 @@
           "-ldl",
           "-lrt",
           "-lX11",
-        ]):
+        ],
+        static = False
+    ):
     """
     Raylib specific cross platform rules.
 
@@ -30,6 +32,7 @@
         deps_linux: Extra deps for Linux.
         linkopts_macos: Extra linkopts for macOS.
         linkopts_linux: Extra linkopts for Linux.
+        static: Make build exectuable static
     """
 
     macos_bin = name + "_macos"
@@ -40,6 +43,7 @@
         srcs = srcs,
         deps = deps + deps_macos,
         linkopts = linkopts_macos,
+        linkstatic = static,
     )
 
     native.cc_binary(
@@ -47,6 +51,7 @@
         srcs = srcs,
         deps = deps + deps_linux,
         linkopts = linkopts_linux,
+        linkstatic = static,
     )
 
     native.alias(