Nuklear
Nuklear copied to clipboard
Getting some strange text clipping behavior when dragging windows around.
The text seems to randomly clip for whatever reason, when I drag the window around my screen. Not entirely sure why.
Most of the code is based on the demo.
Link to the video demonstrating the problem: https://shadowmavericks.com/files/ShareX/tfury_2024-05-25_22-41-40.mp4 Alt: https://github.com/Immediate-Mode-UI/Nuklear/assets/1618721/8099e1ff-450a-486f-9282-a82584c83bbd
My code thus far:
#include <string.h>
#define NK_IMPLEMENTATION
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
//#define NK_UINT_DRAW_INDEX
#include "nuklear.h"
#include "GLRender.h"
struct nk_context ctx;
struct nk_user_font uiFont;
struct nk_colorf bg;
struct tfury_gui_vertex
{
float position[2];
float uv[2];
float col[4];
};
typedef struct
{
SFont *font;
float scale;
} tfury_uifont_info;
struct nk_convert_config config;
struct nk_buffer cmds, vbuf, ebuf;
static float UI_GetMessageWidth(nk_handle handle, float height, const char *text, int len)
{
tfury_uifont_info *fontInfo = handle.ptr;
SFont *font = fontInfo->font;
if (font == NULL)
return 0.0;
float width = TXT_GetMessageWidth((char *)text, font, fontInfo->scale);
return width;
}
static void UI_GetGlyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint)
{
tfury_uifont_info *fontInfo = handle.ptr;
SFont *font = fontInfo->font;
if (font == NULL)
return;
SGlyph scaled = TXT_GetGlyphScaled((char)codepoint, font, fontInfo->scale);
codepoint -= FIRST_CHAR;
if (codepoint < 0)
return;
glyph->width = scaled.width;
glyph->height = scaled.height;
glyph->offset.x = scaled.xoffset;
glyph->offset.y = scaled.yoffset;
glyph->xadvance = scaled.advance;
glyph->uv[0].x = scaled.x / font->texture_x;
glyph->uv[0].y = scaled.y / font->texture_y;
glyph->uv[1].x = (scaled.x + font->glyphs[codepoint].width) / font->texture_x;
glyph->uv[1].y = (scaled.y + font->glyphs[codepoint].height) / font->texture_y;
}
struct nk_user_font UI_LoadUserFont(SFont *font, float scale)
{
struct nk_user_font userFont;
// Todo: Add a function to free this!
tfury_uifont_info *fontInfo = (tfury_uifont_info *)malloc(sizeof(tfury_uifont_info));
fontInfo->font = font;
fontInfo->scale = scale;
userFont.userdata.ptr = fontInfo;
userFont.height = TXT_GetFontInfo(FONTINFO_LINEHEIGHT, font, scale);
userFont.width = UI_GetMessageWidth;
userFont.query = UI_GetGlyph;
userFont.texture.id = font->texture;
return userFont;
}
void UI_Init()
{
uiFont = UI_LoadUserFont(&smallfont, 0.6);
nk_init_default(&ctx, &uiFont);
static const struct nk_draw_vertex_layout_element vertex_layout[] = {
{NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct tfury_gui_vertex, position)},
{NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct tfury_gui_vertex, uv)},
{NK_VERTEX_COLOR, NK_FORMAT_R32G32B32A32_FLOAT, NK_OFFSETOF(struct tfury_gui_vertex, col)},
{NK_VERTEX_LAYOUT_END}
};
memset(&config, 0, sizeof(config));
config.vertex_layout = vertex_layout;
config.vertex_size = sizeof(struct tfury_gui_vertex);
config.vertex_alignment = NK_ALIGNOF(struct tfury_gui_vertex);
config.tex_null.texture.id = whiteTexture;
config.tex_null.uv.x = 0.0;
config.tex_null.uv.y = 0.0;
config.circle_segment_count = 22;
config.curve_segment_count = 22;
config.arc_segment_count = 22;
config.global_alpha = 1.0f;
config.shape_AA = NK_ANTI_ALIASING_ON;
config.line_AA = NK_ANTI_ALIASING_OFF;
nk_buffer_init_default(&cmds);
}
void UI_Draw()
{
GLsizei vstr = sizeof(struct tfury_gui_vertex);
GLint viewport_save[4];
glGetIntegerv(GL_VIEWPORT, viewport_save);
glViewport(0, 0, gamePIXX, gamePIXY);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0f, gamePIXX, gamePIXY, 0.0f, -1.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
/* GUI */
if (nk_begin(&ctx, "Demo", nk_rect(50, (gamePIXY/2.0f)-125.0f, 230, 250),
NK_WINDOW_BORDER | NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE |
NK_WINDOW_MINIMIZABLE | NK_WINDOW_TITLE))
{
enum
{
EASY, HARD
};
static int op = EASY;
static int property = 20;
nk_layout_row_static(&ctx, 30, 128, 1);
if (nk_button_label(&ctx, "button"))
fprintf(stdout, "button pressed\n");
nk_layout_row_dynamic(&ctx, 30, 2);
if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY;
if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD;
nk_layout_row_dynamic(&ctx, 25, 1);
nk_property_int(&ctx, "Compression:", 0, &property, 100, 10, 1);
nk_layout_row_dynamic(&ctx, 20, 1);
nk_label(&ctx, "background:", NK_TEXT_LEFT);
nk_layout_row_dynamic(&ctx, 25, 1);
if (nk_combo_begin_color(&ctx, nk_rgb_cf(bg), nk_vec2(nk_widget_width(&ctx), 400)))
{
nk_layout_row_dynamic(&ctx, 120, 1);
bg = nk_color_picker(&ctx, bg, NK_RGBA);
nk_layout_row_dynamic(&ctx, 25, 1);
bg.r = nk_propertyf(&ctx, "#R:", 0, bg.r, 1.0f, 0.01f, 0.005f);
bg.g = nk_propertyf(&ctx, "#G:", 0, bg.g, 1.0f, 0.01f, 0.005f);
bg.b = nk_propertyf(&ctx, "#B:", 0, bg.b, 1.0f, 0.01f, 0.005f);
bg.a = nk_propertyf(&ctx, "#A:", 0, bg.a, 1.0f, 0.01f, 0.005f);
nk_combo_end(&ctx);
}
}
nk_end(&ctx);
nk_buffer_init_default(&vbuf);
nk_buffer_init_default(&ebuf);
nk_convert(&ctx, &cmds, &vbuf, &ebuf, &config);
{
const struct tfury_gui_vertex *vertices = (struct tfury_gui_vertex *)nk_buffer_memory_const(&vbuf);
glVertexAttribPointer(VERTEXATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, vstr, &vertices[0].position);
glVertexAttribPointer(VERTEXATTRIB_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, vstr, &vertices[0].uv);
glVertexAttribPointer(VERTEXATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, vstr, &vertices[0].col);
glEnableVertexAttribArray(VERTEXATTRIB_POSITION);
glEnableVertexAttribArray(VERTEXATTRIB_COLOR);
glEnableVertexAttribArray(VERTEXATTRIB_TEXCOORD0);
}
struct nk_vec2 scale;
scale.x = 1.0f;
scale.y = 1.0f;
glEnable(GL_SCISSOR_TEST);
glEnable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// iterate over and execute each draw command
const struct nk_draw_command *cmd;
const nk_draw_index *offset = NULL;
offset = (const nk_draw_index *)nk_buffer_memory_const(&ebuf);
nk_draw_foreach(cmd, &ctx, &cmds)
{
if (!cmd->elem_count)
continue;
glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id);
glScissor(
(GLint)(cmd->clip_rect.x * scale.x),
(GLint)((gamePIXY - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * scale.y),
(GLint)(cmd->clip_rect.w * scale.x),
(GLint)(cmd->clip_rect.h * scale.y));
glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset);
offset += cmd->elem_count;
}
nk_clear(&ctx);
nk_buffer_clear(&cmds);
nk_buffer_free(&vbuf);
nk_buffer_free(&ebuf);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
RENDER_SetArrays(ARRAY_MAIN);
glViewport(viewport_save[0], viewport_save[1], viewport_save[2], viewport_save[3]);
}
static int UI_ParseEvents(SDL_Event *evt)
{
switch(evt->type)
{
case SDL_KEYUP: /* KEYUP & KEYDOWN share same routine */
case SDL_KEYDOWN:
{
int down = evt->type == SDL_KEYDOWN;
switch(evt->key.keysym.sym)
{
case SDLK_RSHIFT: /* RSHIFT & LSHIFT share same routine */
case SDLK_LSHIFT: nk_input_key(&ctx, NK_KEY_SHIFT, down); break;
case SDLK_DELETE: nk_input_key(&ctx, NK_KEY_DEL, down); break;
case SDLK_RETURN: nk_input_key(&ctx, NK_KEY_ENTER, down); break;
case SDLK_TAB: nk_input_key(&ctx, NK_KEY_TAB, down); break;
case SDLK_BACKSPACE: nk_input_key(&ctx, NK_KEY_BACKSPACE, down); break;
case SDLK_HOME: nk_input_key(&ctx, NK_KEY_TEXT_START, down);
nk_input_key(&ctx, NK_KEY_SCROLL_START, down); break;
case SDLK_END: nk_input_key(&ctx, NK_KEY_TEXT_END, down);
nk_input_key(&ctx, NK_KEY_SCROLL_END, down); break;
case SDLK_PAGEDOWN: nk_input_key(&ctx, NK_KEY_SCROLL_DOWN, down); break;
case SDLK_PAGEUP: nk_input_key(&ctx, NK_KEY_SCROLL_UP, down); break;
case SDLK_z: nk_input_key(&ctx, NK_KEY_TEXT_UNDO, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_r: nk_input_key(&ctx, NK_KEY_TEXT_REDO, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_c: nk_input_key(&ctx, NK_KEY_COPY, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_v: nk_input_key(&ctx, NK_KEY_PASTE, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_x: nk_input_key(&ctx, NK_KEY_CUT, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_b: nk_input_key(&ctx, NK_KEY_TEXT_LINE_START, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_e: nk_input_key(&ctx, NK_KEY_TEXT_LINE_END, down && keyset[SDL_SCANCODE_LCTRL]); break;
case SDLK_UP: nk_input_key(&ctx, NK_KEY_UP, down); break;
case SDLK_DOWN: nk_input_key(&ctx, NK_KEY_DOWN, down); break;
case SDLK_LEFT:
if (keyset[SDL_SCANCODE_LCTRL])
nk_input_key(&ctx, NK_KEY_TEXT_WORD_LEFT, down);
else nk_input_key(&ctx, NK_KEY_LEFT, down);
break;
case SDLK_RIGHT:
if (keyset[SDL_SCANCODE_LCTRL])
nk_input_key(&ctx, NK_KEY_TEXT_WORD_RIGHT, down);
else nk_input_key(&ctx, NK_KEY_RIGHT, down);
break;
}
}
return 1;
case SDL_MOUSEBUTTONUP: /* MOUSEBUTTONUP & MOUSEBUTTONDOWN share same routine */
case SDL_MOUSEBUTTONDOWN:
{
int down = evt->type == SDL_MOUSEBUTTONDOWN;
const int x = evt->button.x, y = evt->button.y;
switch(evt->button.button)
{
case SDL_BUTTON_LEFT:
if (evt->button.clicks > 1)
nk_input_button(&ctx, NK_BUTTON_DOUBLE, x, y, down);
nk_input_button(&ctx, NK_BUTTON_LEFT, x, y, down); break;
case SDL_BUTTON_MIDDLE: nk_input_button(&ctx, NK_BUTTON_MIDDLE, x, y, down); break;
case SDL_BUTTON_RIGHT: nk_input_button(&ctx, NK_BUTTON_RIGHT, x, y, down); break;
}
}
return 1;
case SDL_MOUSEMOTION:
if (ctx.input.mouse.grabbed) {
int x = (int)ctx.input.mouse.prev.x, y = (int)ctx.input.mouse.prev.y;
nk_input_motion(&ctx, x + evt->motion.xrel, y + evt->motion.yrel);
}
else nk_input_motion(&ctx, evt->motion.x, evt->motion.y);
return 1;
case SDL_TEXTINPUT:
{
nk_glyph glyph;
memcpy(glyph, evt->text.text, NK_UTF_SIZE);
nk_input_glyph(&ctx, glyph);
}
return 1;
case SDL_MOUSEWHEEL:
nk_input_scroll(&ctx,nk_vec2((float)evt->wheel.x,(float)evt->wheel.y));
return 1;
}
return 0;
}
int UI_HandleEvents(SDL_Event *evt)
{
nk_input_begin(&ctx);
int result = UI_ParseEvents(evt);
nk_input_end(&ctx);
return result;
}