1111 lines
28 KiB
C

#include "emgui.h"
#include "emgui_internal.h"
#include "memory/LinearAllocator.h"
#include "GFXBackend.h"
#include "MicroKnightFont.h"
#include "External/stb_truetype.h"
#include "External/stb_image.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(emguiGUI_MACOSX)
#include <sys/syslimits.h>
#else
#define PATH_MAX 1024
#endif
struct EmguiImage;
typedef struct MouseState
{
int x;
int y;
int down;
} MouseState;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct EmguiState
{
MouseState mouse;
int hotItem;
int activeItem;
int kbdItem;
int keyCode;
int keyMod;
int lastWidget;
} EmguiState;
struct EmguiImageCache
{
uint64_t hash;
uint64_t handle;
int32_t width;
int32_t height;
};
enum
{
MAX_IMAGES = 1024,
EM_FONTBITMAP_WIDTH = 512,
EM_FONTBITMAP_HEIGHT = 512,
};
static LinearAllocator s_allocator;
static LinearAllocatorRewindPoint s_rewindPoint;
static int g_loadedFontsCount = 1;
LoadedFont g_loadedFonts[MAX_FONTS];
EmguiState g_emguiGuiState;
uint32_t g_controlId;
uint32_t g_currentFont = 1;
uint32_t g_temp;
uint32_t g_defaultFont;
static int s_activeLayer = 0;
struct RenderData s_renderData;
static struct EmguiImageCache s_imageCache[MAX_IMAGES];
static int s_imageCacheCount;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum GuiPlacementState
{
PLACEMENTSTATE_NONE,
PLACEMENTSTATE_HORIZONAL,
PLACEMENTSTATE_VERTICAL,
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct GuiPlacementInfo
{
enum GuiPlacementState state;
int x;
int y;
} GuiPlacementInfo;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static GuiPlacementInfo g_placementInfo;
EmguiControlInfo g_controls[MAX_CONTROLS];
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static EMGUI_INLINE EmguiControlInfo* newControl()
{
EmguiControlInfo* control = &g_controls[g_controlId];
g_controlId++;
return control;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_beginHorizontalStackPanelXY(int x, int y)
{
g_placementInfo.state = PLACEMENTSTATE_HORIZONAL;
g_placementInfo.x = x;
g_placementInfo.y = y;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_beginVerticalStackPanelXY(int x, int y)
{
g_placementInfo.state = PLACEMENTSTATE_VERTICAL;
g_placementInfo.x = x;
g_placementInfo.y = y;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// taken from:
// http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/142054
//
// djb :: 99.8601 percent coverage (60 collisions out of 42884)
// elf :: 99.5430 percent coverage (196 collisions out of 42884)
// sdbm :: 100.0000 percent coverage (0 collisions out of 42884) (this is the algo used)
// ...
static uint32_t quickHash(const char* string)
{
uint32_t c;
uint32_t hash = 0;
const uint8_t* str = (const uint8_t*)string;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash & 0x7FFFFFFF;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint64_t loadImage(int* width, int* height, void* image, int length, enum EmguiMemoryLocation location)
{
int w = 0, h = 0, c = 0;
void* imageData = 0;
int i, imageCount = s_imageCacheCount;
uint64_t imageId = (uint64_t)~0;
uint64_t hash = (uintptr_t)image;
if (location == EMGUI_LOCATION_FILE)
hash = quickHash((const char*)image);
// See if we have the image loaded already
for (i = 0; i < imageCount; ++i)
{
if (s_imageCache[i].hash == hash)
{
*width = s_imageCache[i].width;
*height = s_imageCache[i].height;
return s_imageCache[i].handle;
}
}
if (location == EMGUI_LOCATION_FILE)
{
imageData = stbi_load(image, &w, &h, &c, 0);
if (!imageData)
printf("EMGUI: Unable to load image %s\n", (char*)image);
}
else if (location == EMGUI_LOCATION_MEMORY)
{
imageData = stbi_load_from_memory(image, length, &w, &h, &c, 0);
if (!imageData)
printf("EMGUI: Unable to load image (from memory %p)\n", image);
}
if (imageData)
imageId = EMGFXBackend_createTexture(imageData, w, h, c);
s_imageCacheCount++;
s_imageCache[imageCount].hash = hash;
s_imageCache[imageCount].handle = imageId;
s_imageCache[imageCount].width = w;
s_imageCache[imageCount].height = h;
*width = s_imageCache[i].width;
*height = s_imageCache[i].height;
return imageId;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void createDefaultFont()
{
struct LoadedFont* loadedFont = &g_loadedFonts[0];
uint8_t* tempColorData;
uint8_t* colorData = tempColorData = (uint8_t*)malloc(128 * 128);
const uint8_t* data = s_microkinghtFontData;
uint32_t i;
// Build new texture
for (i = 0; i < (128 * 128) / 8; ++i)
{
uint8_t color = *data++;
// font data is packed as 1 bit per pixel
*tempColorData++ = ((color >> 7) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 6) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 5) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 4) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 3) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 2) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 1) & 1) ? 0xff : 0;
*tempColorData++ = ((color >> 0) & 1) ? 0xff : 0;
}
loadedFont->handle = EMGFXBackend_createFontTexture(colorData, 128, 128);
strcpy(loadedFont->name, "Microknight");
loadedFont->altLookup = (unsigned short*)&s_microknightLayout[0].x;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int Emgui_loadFontBitmap(const char* buffer, int size, enum EmguiMemoryLocation location,
int rangeStart, int rangeEnd, EmguiFontLayout* layout)
{
struct LoadedFont* loadedFont;
void* imageData = 0;
int fontId = 0, width = 0, height = 0, comp = 0;
// Load the texture in to memory
if (location == EMGUI_LOCATION_FILE)
imageData = stbi_load(buffer, &width, &height, &comp, 0);
else if (location == EMGUI_LOCATION_MEMORY)
imageData = stbi_load_from_memory((void*)buffer, size, &width, &height, &comp, 0);
// make sure we got the image data and that its an 8-bit (alpha only) texture
if (!imageData || comp != 1)
{
free(imageData);
return -1;
}
fontId = g_loadedFontsCount++;
loadedFont = &g_loadedFonts[fontId];
memset(loadedFont, 0, sizeof(LoadedFont));
loadedFont->width = (uint16_t)width;
loadedFont->height = (uint16_t)height;
loadedFont->rangeStart = rangeStart;
loadedFont->rangeEnd = rangeEnd;
loadedFont->handle = EMGFXBackend_createFontTexture(imageData, width, height);
loadedFont->layout = layout;
free(imageData);
return fontId;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Track both y and x size and return a mask of them
uint32_t Emgui_getTextSize(const char* text)
{
uint32_t x_size = 0;
int y_size = 0;
int range_start;
int range_end;
int c;
const LoadedFont* font = &g_loadedFonts[g_currentFont];
if (!font->layout)
return (8 << 16) | strlen(text) * 8;
range_start = font->rangeStart;
range_end = font->rangeEnd;
c = (unsigned char)*text++;
while (c != 0)
{
if (c >= range_start && c < range_end)
{
const int offset = c - range_start;
x_size += font->layout[offset].xadvance;
if (font->layout[offset].height > y_size)
y_size = font->layout[offset].height;
}
c = *text++;
}
return (y_size << 16) | x_size;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Emgui_create()
{
const uint32_t size = 1024 * 1024;
LinearAllocator_create(&s_allocator, malloc(size), size);
createDefaultFont();
g_emguiGuiState.kbdItem = -1;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_begin()
{
g_controlId = 1;
g_controls[0].type = EMGUI_DRAWTYPE_NONE;
memset(&s_renderData, 0, sizeof(s_renderData));
memset(&g_placementInfo, 0, sizeof(GuiPlacementInfo));
s_rewindPoint = LinearAllocator_getRewindPoint(&s_allocator);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static bool Emgui_regionHit(const EmguiControlInfo* control)
{
const int mouse_x = g_emguiGuiState.mouse.x;
const int mouse_y = g_emguiGuiState.mouse.y;
// need to figure out why adding 4 makes this better
const int control_x = control->x + 4;
const int control_y = control->y + 4;
/*
printf("%d %d - %d %d %d %d\n",
mouse_x, mouse_y,
control_x, control_y,
control_x + control->width,
control_y + control->height);
*/
if (mouse_x < control_x ||
mouse_y < control_y ||
mouse_x >= control_x + control->width ||
mouse_y >= control_y + control->height)
{
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_reset()
{
g_emguiGuiState.mouse.x = -1;
g_emguiGuiState.mouse.y = -1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static bool Emgui_regionHitSlider(const EmguiControlInfo* control)
{
const int mouse_x = g_emguiGuiState.mouse.x;
const int mouse_y = g_emguiGuiState.mouse.y;
const int slider_thumb_x = control->sliderThumbX + 4;
const int slider_thumb_y = control->sliderThumbY + 4;
if (mouse_x < slider_thumb_x ||
mouse_y < slider_thumb_y ||
mouse_x >= slider_thumb_x + control->sliderThumbWidth ||
mouse_y >= slider_thumb_y + control->sliderThumbHeight)
{
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_fill(uint32_t color, int x, int y, int w, int h)
{
Emgui_fillGrad(color, color, x, y, w, h);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_fillGrad(uint32_t color0, uint32_t color1, int x, int y, int w, int h)
{
const int active_layer = s_activeLayer;
struct DrawFillCommand* command = LinearAllocator_allocZero(&s_allocator, struct DrawFillCommand);
command->x = x;
command->y = y;
command->width = w;
command->height = h;
command->color0 = color0;
command->color1 = color1;
if (!s_renderData.layers[active_layer].fillCommands)
{
s_renderData.layers[active_layer].fillCommands = command; // first command
s_renderData.layers[active_layer].fillCommandsTail = command;
}
else
{
s_renderData.layers[active_layer].fillCommandsTail->next = command;
s_renderData.layers[active_layer].fillCommandsTail = command;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_drawBorder(uint32_t color0, uint32_t color1, int x, int y, int w, int h)
{
const uint32_t size = 2;
Emgui_fill(color0, x, y, size, h);
Emgui_fill(color0, x, y, w, size);
Emgui_fill(color1, x + w, y, size, h + size);
Emgui_fill(color1, x, y + h, w, size);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_textLabelXY(const char* text, int x, int y)
{
Emgui_drawText(text, x, y, 0xffffffff);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float floatClamp(float value, float low, float high)
{
if (value < low)
value = low;
if (value > high)
value = high;
return value;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Emgui_slider(int x, int y, int w, int h, int start, int end, int largeVal, enum EmguiSliderDirection dir, int itemSpace, int* ivalue)
{
//int thumbPosition = 0;
int controlId = 0;
float value = (float)*ivalue;
float range;
int left, height;
int thumb_width = 0, thumb_height = 0;
EmguiControlInfo* control = 0;
// Setup the control
controlId = (int)g_controlId++;
control = &g_controls[controlId];
control->type = EMGUI_DRAWTYPE_SLIDER;
control->x = x;
control->y = y;
control->width = w;
control->height = h;
// considering how much stuff we have have in the slider we need to calculate how much space the scolling area actually
// is so we can resize the thumb
range = (float)(end - start);
if (dir == EMGUI_SLIDERDIR_HORIZONTAL)
{
float lw = (float)largeVal;
float fw = (float)w;
thumb_width = (int)((lw * fw) / (range + 1.0f));
left = x + (int)((w - thumb_width) * (value / (float)end));
if (thumb_width < 0)
thumb_width = 2;
control->sliderThumbX = left;
control->sliderThumbY = y + 1;
control->sliderThumbWidth = thumb_width;
control->sliderThumbHeight = h;
}
else
{
float lw = (float)largeVal;
float fh = (float)h;
thumb_height = (int) ((lw * fh) / (range + 1.0f));
height = y + (int)((h - thumb_height) * (value / (float)end));
if (thumb_height < 0)
thumb_height = 2;
control->sliderThumbX = x + 1;
control->sliderThumbY = height;
control->sliderThumbWidth = w;
control->sliderThumbHeight = thumb_height;
}
Emgui_fill(Emgui_color32(255, 255, 255, 255),
control->sliderThumbX, control->sliderThumbY,
control->sliderThumbWidth, control->sliderThumbHeight);
if (Emgui_regionHitSlider(control))
{
g_emguiGuiState.hotItem = controlId;
if (g_emguiGuiState.activeItem == 0 && g_emguiGuiState.mouse.down)
g_emguiGuiState.activeItem = controlId;
}
if (g_emguiGuiState.activeItem == controlId)
{
if (dir == EMGUI_SLIDERDIR_HORIZONTAL)
{
int mouseRelative = g_emguiGuiState.mouse.x - (x + thumb_width / 2);
mouseRelative = eclampi(mouseRelative, 0, w - thumb_width - 1);
value = ((float)mouseRelative / (float)(w - thumb_width)) * (float)end;
*ivalue = (int)value;
printf("%f %d (clamp %d) %f\n", (float)mouseRelative / (float)(w), mouseRelative, (w - thumb_width - 1), value);
}
else
{
int mouseRelative = g_emguiGuiState.mouse.y - (y + thumb_height / 2);
mouseRelative = eclampi(mouseRelative, 0, h - thumb_height - 1);
value = ((float)mouseRelative / (float)(h - thumb_height)) * (float)end;
*ivalue = (int)value;
}
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_textLabel(const char* text)
{
uint32_t controlId = 0;
EmguiControlInfo* control = 0;
// Setup the control
controlId = g_controlId++;
control = &g_controls[controlId];
control->type = EMGUI_DRAWTYPE_TEXT;
control->x = g_placementInfo.x;
control->y = g_placementInfo.y;
control->width = 0; //getTextSize(text);
control->height = 9; // fixme
control->text = LinearAllocator_allocString(&s_allocator, text);
control->color = 0;
control->fontId = g_currentFont;
switch (g_placementInfo.state)
{
case PLACEMENTSTATE_NONE :
{
break;
}
case PLACEMENTSTATE_HORIZONAL :
{
g_placementInfo.x += control->width;
break;
}
case PLACEMENTSTATE_VERTICAL :
{
g_placementInfo.y += 9; // TODO: Use correct size from font
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void drawImage(uint64_t imageId, uint32_t color, int x, int y, int w, int h)
{
const int active_layer = s_activeLayer;
struct DrawImageCommand* command = LinearAllocator_alloc(&s_allocator, struct DrawImageCommand);
command->next = 0;
command->imageId = imageId;
command->x = x;
command->y = y;
command->width = w;
command->height = h;
command->color = color;
if (!s_renderData.layers[active_layer].imageCommands)
{
s_renderData.layers[active_layer].imageCommands = command;
s_renderData.layers[active_layer].imageCommandsTail = command;
}
else
{
s_renderData.layers[active_layer].imageCommandsTail->next = command;
s_renderData.layers[active_layer].imageCommandsTail = command;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void drawText(const char* text, bool flipped, int x, int y, uint32_t color)
{
struct DrawTextCommand* temp;
struct DrawTextCommand* command = LinearAllocator_alloc(&s_allocator, struct DrawTextCommand);
command->x = x;
command->y = y;
command->text = LinearAllocator_allocString(&s_allocator, text);
command->color = color;
command->flipped = flipped;
assert(g_currentFont < EMGUI_MAX_FONTS);
temp = s_renderData.layers[s_activeLayer].textCommands[g_currentFont];
s_renderData.layers[s_activeLayer].textCommands[g_currentFont] = command;
command->next = temp;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_drawTextFlipped(const char* text, int x, int y, uint32_t color)
{
drawText(text, true, x, y, color);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_drawText(const char* text, int x, int y, uint32_t color)
{
drawText(text, false, x, y, color);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void* readFileToMemory(const char* filename)
{
int size;
void* fileBuffer;
FILE* file;
file = fopen(filename, "rb");
if (!file)
return false;
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
if (!(fileBuffer = malloc(size)))
{
fclose(file);
return false;
}
fread(fileBuffer, 1, size, file);
fclose(file);
return fileBuffer;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t Emgui_loadFont(const char* ttfFontname, float fontHeight)
{
uint32_t fontId = 0;
struct LoadedFont* loadedFont;
uint8_t* fontBitmap = 0;
void* ttfFontBuffer = readFileToMemory(ttfFontname);
if (!ttfFontBuffer)
return false;
if (g_loadedFontsCount > MAX_FONTS)
return false;
if (!(fontBitmap = malloc(EM_FONTBITMAP_WIDTH * EM_FONTBITMAP_HEIGHT)))
return false;
fontId = g_loadedFontsCount++;
loadedFont = &g_loadedFonts[fontId];
memset(loadedFont, 0, sizeof(LoadedFont));
strncpy(loadedFont->name, ttfFontname, 512);
// TODO: Support support different texturesizes and utf8/non-ascii chars?
stbtt_BakeFontBitmap(ttfFontBuffer, 0, fontHeight, fontBitmap, EM_FONTBITMAP_WIDTH, EM_FONTBITMAP_HEIGHT,
32, 96, loadedFont->cData);
loadedFont->handle = EMGFXBackend_createFontTexture(fontBitmap, EM_FONTBITMAP_WIDTH, EM_FONTBITMAP_HEIGHT);
free(ttfFontBuffer);
free(fontBitmap);
return fontId;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setFont(uint32_t fontId)
{
g_currentFont = fontId;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setDefaultFont()
{
Emgui_setFont(0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static uint32_t genericImageControl(const char* filename)
{
uint32_t controlId = 0;
EmguiControlInfo* control = 0;
struct EmguiImage* image = 0; //loadImage(filename);
if (!image)
return (uint32_t)~0;
// Setup the control
controlId = g_controlId++;
control = &g_controls[controlId];
control->type = EMGUI_DRAWTYPE_IMAGE;
control->x = g_placementInfo.x;
control->y = g_placementInfo.y;
//control->width = image->width;
//control->height = image->height;
//control->imageData = image;
//updatePlacement();
switch (g_placementInfo.state)
{
case PLACEMENTSTATE_NONE :
{
break;
}
case PLACEMENTSTATE_HORIZONAL :
{
//g_placementInfo.x += image->width;
break;
}
case PLACEMENTSTATE_VERTICAL :
{
//g_placementInfo.y += image->height;
break;
}
}
return controlId;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_staticImage(const char* filename)
{
genericImageControl(filename);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
static void highlightControl(EmguiControlInfo* control)
{
Emgui_fill(0xb0ffffff, control->x, control->y, control->width, control->height);
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Emgui_buttonImage(const char* filename)
{
EmguiControlInfo* control;
int controlId = 0;
controlId = genericImageControl(filename);
if (controlId == ~0)
return false;
control = &g_controls[controlId];
if (Emgui_regionHit(control))
{
g_emguiGuiState.hotItem = controlId;
if (g_emguiGuiState.activeItem == 0 && g_emguiGuiState.mouse.down)
g_emguiGuiState.activeItem = controlId;
//highlightControl(control);
}
if (g_emguiGuiState.mouse.down == 0 && g_emguiGuiState.hotItem == controlId && g_emguiGuiState.activeItem == controlId)
return true;
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Emgui_buttonCoords(const char* text, uint32_t color, int x, int y, int width, int height)
{
EmguiControlInfo* control;
int controlId = 0;
uint32_t textSize = Emgui_getTextSize(text);
uint32_t size_x = textSize & 0xffff;
uint32_t size_y = textSize >> 16;
Emgui_drawBorder(color, color, x, y, width, height);
Emgui_textLabelXY(text, x + (width - size_x) / 2, y + (height - size_y) / 2);
controlId = (int)g_controlId++;
control = &g_controls[controlId];
control->x = x;
control->y = y;
control->width = width;
control->height = height;
control->color = color;
if (Emgui_regionHit(control))
{
g_emguiGuiState.hotItem = controlId;
if (g_emguiGuiState.activeItem == 0 && g_emguiGuiState.mouse.down)
g_emguiGuiState.activeItem = controlId;
}
if (g_emguiGuiState.mouse.down == 0 &&
g_emguiGuiState.hotItem == controlId &&
g_emguiGuiState.activeItem == controlId)
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_radioButtonImage(void* image0, int size0, void* image1, int size1, enum EmguiMemoryLocation location,
uint32_t color, int x, int y, bool* stateIn)
{
EmguiControlInfo* control;
bool state = *stateIn;
int controlId = 0;
int w0 = -1, h0 = -1;
int w1 = -1, h1 = -1;
int w, h;
uint64_t i0 = loadImage(&w0, &h0, image0, size0, location);
uint64_t i1 = loadImage(&w1, &h1, image1, size1, location);
if ((uint64_t)~0 == i0 || (uint64_t)~0 == i1)
{
printf("EMGUI: (radioButton) Unable to create due to fail of loading image(s)\n");
return;
}
controlId = (int)g_controlId++;
control = &g_controls[controlId];
w = state ? w0 : w1;
h = state ? h0 : h1;
control->x = x;
control->y = y;
control->width = w;
control->height = h;
control->color = color;
if (Emgui_regionHit(control))
{
g_emguiGuiState.hotItem = controlId;
if (g_emguiGuiState.activeItem == 0 && g_emguiGuiState.mouse.down)
g_emguiGuiState.activeItem = controlId;
}
if (g_emguiGuiState.mouse.down == 0 &&
g_emguiGuiState.hotItem == controlId &&
g_emguiGuiState.activeItem == controlId)
{
state = !state;
}
if (state)
drawImage(i0, color, x, y, w0, h0);
else
drawImage(i1, color, x, y, w1, h1);
*stateIn = state;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_end()
{
if (g_emguiGuiState.mouse.down == 0)
{
g_emguiGuiState.activeItem = 0;
}
else
{
if (g_emguiGuiState.activeItem == 0)
g_emguiGuiState.activeItem = -1;
}
g_emguiGuiState.keyCode = 0;
EMGFXBackend_render();
// Rewind the linear allocator
LinearAllocator_rewind(&s_allocator, s_rewindPoint);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_editBoxXY(int x, int y, int width, int height, int bufferLength, char* buffer)
{
EmguiControlInfo* control;
int controlId = 0;
const int keyCode = g_emguiGuiState.keyCode;
uint32_t color = Emgui_color32(200, 200, 127, 255);
controlId = (int)g_controlId++;
control = &g_controls[controlId];
control->x = x;
control->y = y;
control->width = width;
control->height = height;
control->color = color;
if (Emgui_regionHit(control))
{
g_emguiGuiState.hotItem = controlId;
if (g_emguiGuiState.activeItem == 0 && g_emguiGuiState.mouse.down)
{
g_emguiGuiState.activeItem = controlId;
g_emguiGuiState.kbdItem = controlId;
}
}
// if we have keyboard active on this control we need to modify the text
if (g_emguiGuiState.kbdItem == controlId && keyCode != 0)
{
int len = strlen(buffer);
switch (keyCode)
{
case EMGUI_KEY_TAB :
{
if (g_emguiGuiState.keyMod & EMGUI_KEY_SHIFT)
{
if (controlId - 1 <= 0)
{
g_emguiGuiState.kbdItem = -1;
g_emguiGuiState.keyCode = 0;
}
else
{
g_emguiGuiState.kbdItem = controlId - 1;
g_emguiGuiState.keyCode = 0;
}
break;
}
else
{
if (controlId + 1 >= 4)
{
g_emguiGuiState.kbdItem = -1;
g_emguiGuiState.keyCode = 0;
}
else
{
g_emguiGuiState.kbdItem = controlId + 1;
g_emguiGuiState.keyCode = 0;
}
}
break;
}
case EMGUI_KEY_ENTER :
{
g_emguiGuiState.kbdItem = -1;
g_emguiGuiState.keyCode = 0;
break;
}
case EMGUI_KEY_BACKSPACE :
{
buffer[emaxi(len - 1, 0)] = 0;
break;
}
default :
{
// Rocket hack:
// Currenty for rocket we only need 0-9 as input (for the edit field
// so we only update here if those are the keys
int offset = emini(bufferLength - 2, len);
if (keyCode >= '0' && keyCode <= '9')
{
buffer[offset + 0] = (char)keyCode;
buffer[offset + 1] = 0;
}
break;
}
}
}
Emgui_fillGrad(Emgui_color32(70, 70, 70, 255), Emgui_color32(30, 30, 30, 255), x, y, width, height);
Emgui_drawText(buffer, x + 2, y, color);
if (g_emguiGuiState.kbdItem == controlId)
{
int text_size = Emgui_getTextSize(buffer) & 0xffff;
uint32_t color0 = Emgui_color32(200, 200, 127, 127);
uint32_t color1 = Emgui_color32(200, 200, 127, 127);
Emgui_drawBorder(color0, color1, x, y, width, height);
Emgui_fill(Emgui_color32(200, 200, 127, 200), x + text_size + 2, y + 2, 2, height - 2);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_sendKeyinput(int keyCode, int modifier)
{
g_emguiGuiState.keyCode = keyCode;
g_emguiGuiState.keyMod = modifier;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setMousePos(int posX, int posY)
{
g_emguiGuiState.mouse.x = posX;
g_emguiGuiState.mouse.y = posY;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setMouseLmb(int state)
{
g_emguiGuiState.mouse.down = state;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Emgui_hasKeyboardFocus()
{
return g_emguiGuiState.kbdItem != -1;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setFirstControlFocus()
{
g_emguiGuiState.kbdItem = 1;
g_emguiGuiState.keyCode = 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setLayer(int layer)
{
s_activeLayer = layer;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Emgui_setScissor(int x, int y, int w, int h)
{
const int active_layer = s_activeLayer;
s_renderData.layers[active_layer].scissor.x = x;
s_renderData.layers[active_layer].scissor.y = y;
s_renderData.layers[active_layer].scissor.width = w;
s_renderData.layers[active_layer].scissor.height = h;
}