Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(HELP PLEASE!!! Also, potential bugfix supplied.) Getting some strange text clipping behavior when dragging windows around. #646

Open
StrikerMan780 opened this issue May 26, 2024 · 8 comments

Comments

@StrikerMan780
Copy link

StrikerMan780 commented May 26, 2024

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
Copy link
Author

StrikerMan780 commented May 28, 2024

I need some help with this one if anyone can. I've been at this one for a few days now and I'm losing my mind. Video in first post demonstrating what's going on.

@StrikerMan780
Copy link
Author

StrikerMan780 commented Sep 13, 2024

image image 2

Help? Please? This problem is still happening with the latest version, and I've been struggling with it since getting the library back in May. I'm running out of ideas, and time. Going to have to abandon Nuklear before long if there's no solution.

@StrikerMan780 StrikerMan780 changed the title Getting some strange text clipping behavior when dragging windows around. (HELP PLEASE!!) Getting some strange text clipping behavior when dragging windows around. Sep 13, 2024
@StrikerMan780
Copy link
Author

StrikerMan780 commented Sep 13, 2024

I'm noticing the biggest culprits are buttons and sliders. If I just use labels, it's a tiny bit less prone to this problem.

@StrikerMan780
Copy link
Author

And upon even further inspection, it seems to be related more specifically to NK_TEXT_CENTERED. There seems to be some kind of bug with how it calculates the width of the element. Because if I use centered text with labels, it behaves exactly like buttons do. (Aka. Borked)

@StrikerMan780
Copy link
Author

If I comment out line 23660 in nuklear.h, in nk_widget_text. The line that reads: if (label.w >= label.x) label.w -= label.x;

It seemingly fixes the bug. Consider giving this a look over.

@StrikerMan780 StrikerMan780 changed the title (HELP PLEASE!!) Getting some strange text clipping behavior when dragging windows around. (HELP PLEASE!!! Also, potential bugfix supplied.) Getting some strange text clipping behavior when dragging windows around. Sep 14, 2024
@fkallevik
Copy link

@StrikerMan780 I am having this exact issue in my project as well. But it doesn't happen in the allegro5 demo I tested, so that indicates to me that I am doing something wrong, but idk..

This exhibits the strange text clipping

Screen.Recording.2024-11-09.at.04.10.41.mov

This works as expected

Screen.Recording.2024-11-09.at.04.11.38.mov

@fkallevik
Copy link

I managed to fix this by correcting the nk.height value I set in my_font_create_from_file and the width returned from my_font_get_text_width. In my case I was scaling them to handle high DPI, so I just changed them to return unscaled values, and then I only apply scaling factor when rendering the text in NK_COMMAND_TEXT.

No strange clipping anymore🎉
https://github.com/user-attachments/assets/e59acbc5-a66d-4b37-bf10-16a06cb3fbb1

@StrikerMan780
Copy link
Author

Still can't find a way to fix it on my end without altering that line in Nuklear. The values I return from my text width functions are correct, it's the width of the text in pixels.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants