1111 lines
28 KiB
C
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;
|
|
}
|
|
|