Nuklear icon indicating copy to clipboard operation
Nuklear copied to clipboard

Getting some strange text clipping behavior when dragging windows around.

Open StrikerMan780 opened this issue 3 months ago • 1 comments

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;
}

StrikerMan780 avatar May 26 '24 01:05 StrikerMan780