diff --git a/demo/sdl3_renderer/Makefile b/demo/sdl3_renderer/Makefile new file mode 100644 index 00000000..945ce25c --- /dev/null +++ b/demo/sdl3_renderer/Makefile @@ -0,0 +1,41 @@ +# this Makefile is specific to GNU Make and GCC'compatible compilers + +PKG_CONFIG ?= $(shell command -v pkg-config) +ifeq (,$(PKG_CONFIG)) + $(error missing pkg-config utility!) +endif + +PKG_SDL3 ?= sdl3 +ifeq (,$(shell $(PKG_CONFIG) --path $(PKG_SDL3))) + $(error $(PKG_CONFIG) could not find: $(PKG_SDL3)) +endif + +OS ?= $(shell uname -s) +BINEXT-Windows_NT = .exe +BINEXT ?= $(BINEXIT-$(OS)) + +TEMPDIR ?= ./bin +BIN ?= $(TEMPDIR)/demo$(BINEXT) + +CFLAGS += -std=c89 -Wall -Wextra -Wpedantic +CFLAGS += -O2 +#CFLAGS += -O0 -g +#CFLAGS += -fsanitize=address +#CFLAGS += -fsanitize=undefined +CFLAGS += $(shell $(PKG_CONFIG) $(PKG_SDL3) --cflags) + +LIBS += -lm +LIBS += $(shell $(PKG_CONFIG) $(PKG_SDL3) --libs) + +DEP ?= $(BIN).d + +SRC = main.c + +$(BIN): + mkdir -p $(dir $@) + $(CC) $(SRC) -o $@ -MD -MF $(DEP) $(CFLAGS) $(LIBS) + +$(BIN): $(SRC) ./Makefile ./nuklear_sdl3_renderer.h ./../../nuklear.h + +-include $(DEP) + diff --git a/demo/sdl3_renderer/main.c b/demo/sdl3_renderer/main.c new file mode 100644 index 00000000..1d8cdce7 --- /dev/null +++ b/demo/sdl3_renderer/main.c @@ -0,0 +1,416 @@ +/* nuklear - public domain */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* This demo uses "main callbacks" which are new in SDL3 + * Those provide highly portable entry point and event loop for the app + * see: https://wiki.libsdl.org/SDL3/README-main-functions + * */ +#define SDL_MAIN_USE_CALLBACKS +#include + +/* =============================================================== + * + * CONFIG + * + * ===============================================================*/ + +/* optional: sdl3_renderer does not need any of these defines + * (but some examples might need them, so be careful) */ +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_INCLUDE_STANDARD_IO + +/* note that sdl3_renderer comes with nk_sdl_style_set_debug_font() + * so you may wish to use that instead of font baking */ +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT + +/* note that sdl3_renderer comes with nk_sdl_allocator() + * and you probably want to use that allocator instead of the default ones */ +/*#define NK_INCLUDE_DEFAULT_ALLOCATOR*/ + +/* mandatory: sdl3_renderer depends on those defines */ +#define NK_INCLUDE_COMMAND_USERDATA +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT + + +/* We can re-use the types provided by SDL which are extremely portable, + * so there is no need for Nuklear to detect those on its own */ +/*#define NK_INCLUDE_FIXED_TYPES*/ +#ifndef NK_INCLUDE_FIXED_TYPES + #define NK_INT8 Sint8 + #define NK_UINT8 Uint8 + #define NK_INT16 Sint16 + #define NK_UINT16 Uint16 + #define NK_INT32 Sint32 + #define NK_UINT32 Uint32 + /* SDL guarantees 'uintptr_t' typedef */ + #define NK_SIZE_TYPE uintptr_t + #define NK_POINTER_TYPE uintptr_t +#endif + +/* FIXME: We could also use the `bool` symbol provided by SDL, + * but this is currently broken due to internal Nuklear issue, see: + * https://github.com/Immediate-Mode-UI/Nuklear/issues/849 + * */ +#define NK_INCLUDE_STANDARD_BOOL +#ifndef NK_INCLUDE_STANDARD_BOOL + #define NK_BOOL bool +#endif + +/* We can re-use various portable libc functions provided by SDL */ +#define NK_ASSERT(condition) SDL_assert(condition) +#define NK_STATIC_ASSERT(exp) SDL_COMPILE_TIME_ASSERT(, exp) +#define NK_MEMSET(dst, c, len) SDL_memset(dst, c, len) +#define NK_MEMCPY(dst, src, len) SDL_memcpy(dst, src, len) +#define NK_VSNPRINTF(s, n, f, a) SDL_vsnprintf(s, n, f, a) +#define NK_STRTOD(str, endptr) SDL_strtod(str, endptr) + +/* sadly, SDL3 does not provide "dtoa" (only integer version) */ +/*#define NK_DTOA (str, d)*/ + +/* SDL can also provide us with math functions, but beware that Nuklear's own + * implementation can be slightly faster at the cost of some precision */ +#define NK_INV_SQRT(f) (1.0f / SDL_sqrtf(f)) +#define NK_SIN(f) SDL_sinf(f) +#define NK_COS(f) SDL_cosf(f) + +/* HACK: Nuklear pulls two stb libraries in order to use font baking + * those libraries pull in some libc headers internally, creating a linkage dependency, + * so you’ll most likely want to use SDL symbols instead */ +#define STBTT_ifloor(x) ((int)SDL_floor(x)) +#define STBTT_iceil(x) ((int)SDL_ceil(x)) +#define STBTT_sqrt(x) SDL_sqrt(x) +#define STBTT_pow(x,y) SDL_pow(x,y) +#define STBTT_fmod(x,y) SDL_fmod(x,y) +#define STBTT_cos(x) SDL_cosf(x) +#define STBTT_acos(x) SDL_acos(x) +#define STBTT_fabs(x) SDL_fabs(x) +#define STBTT_assert(x) SDL_assert(x) +#define STBTT_strlen(x) SDL_strlen(x) +#define STBTT_memcpy SDL_memcpy +#define STBTT_memset SDL_memset +#define stbtt_uint8 Uint8 +#define stbtt_int8 Sint8 +#define stbtt_uint16 Uint16 +#define stbtt_int16 Sint16 +#define stbtt_uint32 Uint32 +#define stbtt_int32 Sint32 +#define STBRP_SORT SDL_qsort +#define STBRP_ASSERT SDL_assert +/* There is no need to define STBTT_malloc/STBTT_free macros + * Nuklear will define those to user-provided nk_allocator */ + + +#define NK_IMPLEMENTATION +#include "../../nuklear.h" +#define NK_SDL3_RENDERER_IMPLEMENTATION +#include "nuklear_sdl3_renderer.h" + +#define WINDOW_WIDTH 1200 +#define WINDOW_HEIGHT 800 + +/* =============================================================== + * + * EXAMPLE + * + * ===============================================================*/ +/* These are some code examples to provide a small overview of what can be + * done with this library. To try out an example uncomment the defines */ +/*#define INCLUDE_ALL */ +/*#define INCLUDE_STYLE */ +/*#define INCLUDE_CALCULATOR */ +/*#define INCLUDE_CANVAS */ +#define INCLUDE_OVERVIEW +/*#define INCLUDE_CONFIGURATOR */ +/*#define INCLUDE_NODE_EDITOR */ + +#ifdef INCLUDE_ALL + #define INCLUDE_STYLE + #define INCLUDE_CALCULATOR + #define INCLUDE_CANVAS + #define INCLUDE_OVERVIEW + #define INCLUDE_CONFIGURATOR + #define INCLUDE_NODE_EDITOR +#endif + +#ifdef INCLUDE_STYLE + #include "../../demo/common/style.c" +#endif +#ifdef INCLUDE_CALCULATOR + #include "../../demo/common/calculator.c" +#endif +#ifdef INCLUDE_CANVAS + #include "../../demo/common/canvas.c" +#endif +#ifdef INCLUDE_OVERVIEW + #include "../../demo/common/overview.c" +#endif +#ifdef INCLUDE_CONFIGURATOR + #include "../../demo/common/style_configurator.c" +#endif +#ifdef INCLUDE_NODE_EDITOR + #include "../../demo/common/node_editor.c" +#endif + +/* =============================================================== + * + * DEMO + * + * ===============================================================*/ + +struct nk_sdl_app { + SDL_Window* window; + SDL_Renderer* renderer; + struct nk_context * ctx; + struct nk_colorf bg; + enum nk_anti_aliasing AA; +}; + +static SDL_AppResult +nk_sdl_fail() +{ + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error: %s", SDL_GetError()); + return SDL_APP_FAILURE; +} + +SDL_AppResult +SDL_AppInit(void** appstate, int argc, char* argv[]) +{ + struct nk_sdl_app* app; + struct nk_context* ctx; + float font_scale; + NK_UNUSED(argc); + NK_UNUSED(argv); + + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { + return nk_sdl_fail(); + } + + app = SDL_malloc(sizeof(*app)); + if (app == NULL) { + return nk_sdl_fail(); + } + + if (!SDL_CreateWindowAndRenderer("Nuklear: SDL3 Renderer", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &app->window, &app->renderer)) { + SDL_free(app); + return nk_sdl_fail(); + } + *appstate = app; + + if (!SDL_SetRenderVSync(app->renderer, 1)) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "SDL_SetRenderVSync failed: %s", SDL_GetError()); + } + + app->bg.r = 0.10f; + app->bg.g = 0.18f; + app->bg.b = 0.24f; + app->bg.a = 1.0f; + + + font_scale = 1; + { + /* This scaling logic was kept simple for the demo purpose. + * On some platforms, this might not be the exact scale + * that you want to use. For more information, see: + * https://wiki.libsdl.org/SDL3/README-highdpi */ + const float scale = SDL_GetWindowDisplayScale(app->window); + SDL_SetRenderScale(app->renderer, scale, scale); + font_scale = scale; + } + + ctx = nk_sdl_init(app->window, app->renderer, nk_sdl_allocator()); + app->ctx = ctx; + +#if 0 + { + /* If you don't want to use advanced Nuklear font baking API + * you can use simple ASCII debug font provided by SDL + * just change the `#if 0` above to `#if 1` */ + nk_sdl_style_set_debug_font(ctx); + + /* Note that since debug font is extremely small (only 8x8 pixels), + * scaling it does not make much sense. The font would appear blurry. */ + NK_UNUSED(font_scale); + + /* You may wish to change a few style options, here are few recommendations: */ + ctx->style.button.rounding = 0.0f; + ctx->style.menu_button.rounding = 0.0f; + ctx->style.property.rounding = 0.0f; + ctx->style.property.border = 0.0f; + ctx->style.option.border = -1.0f; + ctx->style.checkbox.border = -1.0f; + ctx->style.property.dec_button.border = -2.0f; + ctx->style.property.inc_button.border = -2.0f; + ctx->style.tab.tab_minimize_button.border = -2.0f; + ctx->style.tab.tab_maximize_button.border = -2.0f; + ctx->style.tab.node_minimize_button.border = -2.0f; + ctx->style.tab.node_maximize_button.border = -2.0f; + ctx->style.checkbox.spacing = 5.0f; + + /* It's better to disable anti-aliasing when using small fonts */ + app->AA = NK_ANTI_ALIASING_OFF; + } +#else + { + struct nk_font_atlas *atlas; + struct nk_font_config config = nk_font_config(0); + struct nk_font *font; + + /* set up the font atlas and add desired font; note that font sizes are + * multiplied by font_scale to produce better results at higher DPIs */ + atlas = nk_sdl_font_stash_begin(ctx); + font = nk_font_atlas_add_default(atlas, 13 * font_scale, &config); + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/DroidSans.ttf", 14 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Roboto-Regular.ttf", 16 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/kenvector_future_thin.ttf", 13 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyClean.ttf", 12 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/ProggyTiny.ttf", 10 * font_scale, &config);*/ + /*font = nk_font_atlas_add_from_file(atlas, "../../../extra_font/Cousine-Regular.ttf", 13 * font_scale, &config);*/ + nk_sdl_font_stash_end(ctx); + + /* this hack makes the font appear to be scaled down to the desired + * size and is only necessary when font_scale > 1 */ + font->handle.height /= font_scale; + /*nk_style_load_all_cursors(ctx, atlas->cursors);*/ + nk_style_set_font(ctx, &font->handle); + + app->AA = NK_ANTI_ALIASING_ON; + } +#endif + + return SDL_APP_CONTINUE; +} + +SDL_AppResult +SDL_AppEvent(void *appstate, SDL_Event* event) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + /* You may wish to rescale the renderer and Nuklear during this event. + * Without this the UI and Font could appear too small or too big. + * This is not handled by the demo in order to keep it simple, + * but you may wish to re-bake the Font whenever this happens. */ + SDL_Log("Unhandled scale event! Nuklear may appear blurry"); + return SDL_APP_CONTINUE; + } + + /* Remember to always rescale the event coordinates, + * if your renderer uses custom scale. */ + SDL_ConvertEventToRenderCoordinates(app->renderer, event); + + nk_sdl_handle_event(app->ctx, event); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult +SDL_AppIterate(void *appstate) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + struct nk_context* ctx = app->ctx; + +#ifdef INCLUDE_CONFIGURATOR + static struct nk_color color_table[NK_COLOR_COUNT]; + NK_MEMCPY(color_table, nk_default_color_style, sizeof(color_table)); +#endif + + nk_input_end(ctx); + + /* GUI */ + if (nk_begin(ctx, "Demo", nk_rect(50, 50, 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, 80, 1); + if (nk_button_label(ctx, "button")) { + SDL_Log("button pressed"); + } + 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, 1000, 1, 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(app->bg), nk_vec2(nk_widget_width(ctx),400))) { + nk_layout_row_dynamic(ctx, 120, 1); + app->bg = nk_color_picker(ctx, app->bg, NK_RGBA); + nk_layout_row_dynamic(ctx, 25, 1); + app->bg.r = nk_propertyf(ctx, "#R:", 0, app->bg.r, 1.0f, 0.01f,0.005f); + app->bg.g = nk_propertyf(ctx, "#G:", 0, app->bg.g, 1.0f, 0.01f,0.005f); + app->bg.b = nk_propertyf(ctx, "#B:", 0, app->bg.b, 1.0f, 0.01f,0.005f); + app->bg.a = nk_propertyf(ctx, "#A:", 0, app->bg.a, 1.0f, 0.01f,0.005f); + nk_combo_end(ctx); + } + } + nk_end(ctx); + + /* -------------- EXAMPLES ---------------- */ + #ifdef INCLUDE_CALCULATOR + calculator(ctx); + #endif + #ifdef INCLUDE_CANVAS + canvas(ctx); + #endif + #ifdef INCLUDE_OVERVIEW + overview(ctx); + #endif + #ifdef INCLUDE_CONFIGURATOR + style_configurator(ctx, color_table); + #endif + #ifdef INCLUDE_NODE_EDITOR + node_editor(ctx); + #endif + /* ----------------------------------------- */ + + SDL_SetRenderDrawColorFloat(app->renderer, app->bg.r, app->bg.g, app->bg.b, app->bg.a); + SDL_RenderClear(app->renderer); + + nk_sdl_render(ctx, app->AA); + nk_sdl_update_TextInput(ctx); + + /* show if TextInput is active for debug purpose. Feel free to remove this. */ + SDL_SetRenderDrawColor(app->renderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_RenderDebugTextFormat(app->renderer, 10, 10, "TextInputActive? %s", + SDL_TextInputActive(app->window) ? "Yes" : "No"); + + SDL_RenderPresent(app->renderer); + + nk_input_begin(ctx); + return SDL_APP_CONTINUE; +} + +void +SDL_AppQuit(void* appstate, SDL_AppResult result) +{ + struct nk_sdl_app* app = (struct nk_sdl_app*)appstate; + NK_UNUSED(result); + + if (app) { + nk_sdl_shutdown(app->ctx); + SDL_DestroyRenderer(app->renderer); + SDL_DestroyWindow(app->window); + SDL_free(app); + } +} + diff --git a/demo/sdl3_renderer/nuklear_sdl3_renderer.h b/demo/sdl3_renderer/nuklear_sdl3_renderer.h new file mode 100644 index 00000000..25698ea9 --- /dev/null +++ b/demo/sdl3_renderer/nuklear_sdl3_renderer.h @@ -0,0 +1,707 @@ +/* nuklear - public domain */ + +/* + * ============================================================== + * + * API + * + * =============================================================== + */ + +#ifndef NK_SDL3_RENDERER_H_ +#define NK_SDL3_RENDERER_H_ + +#if SDL_MAJOR_VERSION < 3 + #error "nk_sdl3_renderer requires at least SDL 3.0.0" +#endif +#ifndef NK_INCLUDE_COMMAND_USERDATA + #error "nk_sdl3_renderer requires the NK_INCLUDE_COMMAND_USERDATA define" +#endif +#ifndef NK_INCLUDE_VERTEX_BUFFER_OUTPUT + #error "nk_sdl3_renderer requires the NK_INCLUDE_VERTEX_BUFFER_OUTPUT define" +#endif + +/* We have to redefine it because demos do not include any headers + * This is the same default value as the one from "src/nuklear_internal.h" */ +#ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE + #define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) +#endif + +NK_API struct nk_context* nk_sdl_init(SDL_Window *win, SDL_Renderer *renderer, struct nk_allocator allocator); +#ifdef NK_INCLUDE_FONT_BAKING +NK_API struct nk_font_atlas* nk_sdl_font_stash_begin(struct nk_context* ctx); +NK_API void nk_sdl_font_stash_end(struct nk_context* ctx); +#endif +NK_API int nk_sdl_handle_event(struct nk_context* ctx, SDL_Event *evt); +NK_API void nk_sdl_render(struct nk_context* ctx, enum nk_anti_aliasing); +NK_API void nk_sdl_update_TextInput(struct nk_context* ctx); +NK_API void nk_sdl_shutdown(struct nk_context* ctx); +NK_API nk_handle nk_sdl_get_userdata(struct nk_context* ctx); +NK_API void nk_sdl_set_userdata(struct nk_context* ctx, nk_handle userdata); +NK_API void nk_sdl_style_set_debug_font(struct nk_context* ctx); +NK_API struct nk_allocator nk_sdl_allocator(void); + +#endif /* NK_SDL3_RENDERER_H_ */ + +/* + * ============================================================== + * + * IMPLEMENTATION + * + * =============================================================== + */ +#ifdef NK_SDL3_RENDERER_IMPLEMENTATION +#ifndef NK_SDL3_RENDERER_IMPLEMENTATION_ONCE +#define NK_SDL3_RENDERER_IMPLEMENTATION_ONCE + +#ifndef NK_SDL_DOUBLE_CLICK_LO +#define NK_SDL_DOUBLE_CLICK_LO 0.02 +#endif +#ifndef NK_SDL_DOUBLE_CLICK_HI +#define NK_SDL_DOUBLE_CLICK_HI 0.2 +#endif + +struct nk_sdl_device { + struct nk_buffer cmds; + struct nk_draw_null_texture tex_null; + SDL_Texture *font_tex; +}; + +struct nk_sdl_vertex { + float position[2]; + float uv[2]; + float col[4]; +}; + +struct nk_sdl { + SDL_Window *win; + SDL_Renderer *renderer; + struct nk_user_font* debug_font; + struct nk_sdl_device ogl; + struct nk_context ctx; +#ifdef NK_INCLUDE_FONT_BAKING + struct nk_font_atlas atlas; +#endif + struct nk_allocator allocator; + nk_handle userdata; + Uint64 last_left_click; + Uint64 last_render; + bool insert_toggle; + bool edit_was_active; +}; + +NK_API nk_handle +nk_sdl_get_userdata(struct nk_context* ctx) { + struct nk_sdl* sdl; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + return sdl->userdata; +} + +NK_API void +nk_sdl_set_userdata(struct nk_context* ctx, nk_handle userdata) { + struct nk_sdl* sdl; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + sdl->userdata = userdata; +} + +NK_INTERN void * +nk_sdl_alloc(nk_handle user, void *old, nk_size size) +{ + NK_UNUSED(user); + /* FIXME: nk_sdl_alloc should use SDL_realloc here, not SDL_malloc + * but this could cause a double-free due to bug within Nuklear, see: + * https://github.com/Immediate-Mode-UI/Nuklear/issues/768 + * */ +#if 0 + return SDL_realloc(old, size); +#else + NK_UNUSED(old); + return SDL_malloc(size); +#endif +} + +NK_INTERN void +nk_sdl_free(nk_handle user, void *old) +{ + NK_UNUSED(user); + SDL_free(old); +} + +NK_API struct nk_allocator +nk_sdl_allocator() +{ + struct nk_allocator allocator; + allocator.userdata.ptr = 0; + allocator.alloc = nk_sdl_alloc; + allocator.free = nk_sdl_free; + return allocator; +} + +NK_INTERN void +nk_sdl_device_upload_atlas(struct nk_context* ctx, const void *image, int width, int height) +{ + struct nk_sdl* sdl; + NK_ASSERT(ctx); + NK_ASSERT(image); + NK_ASSERT(width > 0); + NK_ASSERT(height > 0); + + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + + /* Clean up if the texture already exists. */ + if (sdl->ogl.font_tex != NULL) { + SDL_DestroyTexture(sdl->ogl.font_tex); + sdl->ogl.font_tex = NULL; + } + + sdl->ogl.font_tex = SDL_CreateTexture(sdl->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); + NK_ASSERT(sdl->ogl.font_tex); + SDL_UpdateTexture(sdl->ogl.font_tex, NULL, image, 4 * width); + SDL_SetTextureBlendMode(sdl->ogl.font_tex, SDL_BLENDMODE_BLEND); +} + +NK_API void +nk_sdl_update_TextInput(struct nk_context* ctx) +{ + struct nk_sdl* sdl; + bool active; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + + /* Determine if Nuklear is using any top-level "edit" widget. + * Popups take higher priority because they block any incomming input. + * This will not work, if the widget is not updating context state properly. */ + if (!ctx->active) + active = false; + else if (ctx->active->popup.win) + active = ctx->active->popup.win->edit.active; + else + active = ctx->active->edit.active; + + /* decide, if TextInputActive should be unchanged/stoped/started + * and change its state accordingly for owned SDL Window */ + if (active != sdl->edit_was_active) + { + const bool window_edit_active = SDL_TextInputActive(sdl->win); + + /* If you ever hit this check, it means that the demo and your app + * (or something else) are all trying to manage TextInputActive state. + * This can cause subtle bugs where the state won't be what you expect. + * You can safely remove this assert and the demo will keep working, + * but make sure it does not cause any issues for you */ + NK_ASSERT(window_edit_active == sdl->edit_was_active && "something else changed TextInputActive state for this Window"); + + if (!window_edit_active && !sdl->edit_was_active && active) + SDL_StartTextInput(sdl->win); + else if (window_edit_active && sdl->edit_was_active && !active) + SDL_StopTextInput(sdl->win); + sdl->edit_was_active = active; + } + + /* FIXME: + * for full SDL3 integration, you also need to find current edit widget + * bounds and the text cursor offset, and pass this data into SDL_SetTextInputArea. + * This is currently not possible to do safely as Nuklear does not support it. + * https://wiki.libsdl.org/SDL3/SDL_SetTextInputArea + * https://github.com/Immediate-Mode-UI/Nuklear/pull/857 + */ +} + +NK_API void +nk_sdl_render(struct nk_context* ctx, enum nk_anti_aliasing AA) +{ + /* setup global state */ + struct nk_sdl* sdl; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + + { /* setup internal delta time that Nuklear needs for animations */ + const Uint64 ticks = SDL_GetTicks(); + ctx->delta_time_seconds = (float)(ticks - sdl->last_render) / 1000.0f; + sdl->last_render = ticks; + } + + { + SDL_Rect saved_clip; + bool clipping_enabled; + int vs = sizeof(struct nk_sdl_vertex); + size_t vp = NK_OFFSETOF(struct nk_sdl_vertex, position); + size_t vt = NK_OFFSETOF(struct nk_sdl_vertex, uv); + size_t vc = NK_OFFSETOF(struct nk_sdl_vertex, col); + + /* convert from command queue into draw list and draw to screen */ + const struct nk_draw_command *cmd; + const nk_draw_index *offset = NULL; + struct nk_buffer vbuf, ebuf; + + /* fill converting configuration */ + struct nk_convert_config config; + static const struct nk_draw_vertex_layout_element vertex_layout[] = { + {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, position)}, + {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, uv)}, + {NK_VERTEX_COLOR, NK_FORMAT_R32G32B32A32_FLOAT, NK_OFFSETOF(struct nk_sdl_vertex, col)}, + {NK_VERTEX_LAYOUT_END} + }; + NK_MEMSET(&config, 0, sizeof(config)); + config.vertex_layout = vertex_layout; + config.vertex_size = sizeof(struct nk_sdl_vertex); + config.vertex_alignment = NK_ALIGNOF(struct nk_sdl_vertex); + config.tex_null = sdl->ogl.tex_null; + config.circle_segment_count = 22; + config.curve_segment_count = 22; + config.arc_segment_count = 22; + config.global_alpha = 1.0f; + config.shape_AA = AA; + config.line_AA = AA; + + /* convert shapes into vertexes */ + nk_buffer_init(&vbuf, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE); + nk_buffer_init(&ebuf, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE); + nk_convert(&sdl->ctx, &sdl->ogl.cmds, &vbuf, &ebuf, &config); + + /* iterate over and execute each draw command */ + offset = (const nk_draw_index*)nk_buffer_memory_const(&ebuf); + + clipping_enabled = SDL_RenderClipEnabled(sdl->renderer); + SDL_GetRenderClipRect(sdl->renderer, &saved_clip); + + nk_draw_foreach(cmd, &sdl->ctx, &sdl->ogl.cmds) + { + if (!cmd->elem_count) continue; + + { + SDL_Rect r; + r.x = cmd->clip_rect.x; + r.y = cmd->clip_rect.y; + r.w = cmd->clip_rect.w; + r.h = cmd->clip_rect.h; + SDL_SetRenderClipRect(sdl->renderer, &r); + } + + { + const void *vertices = nk_buffer_memory_const(&vbuf); + + SDL_RenderGeometryRaw( + sdl->renderer, + (SDL_Texture *)cmd->texture.ptr, + (const float*)((const nk_byte*)vertices + vp), vs, + (const SDL_FColor*)((const nk_byte*)vertices + vc), vs, + (const float*)((const nk_byte*)vertices + vt), vs, + (vbuf.needed / vs), + (void *) offset, cmd->elem_count, 2); + + offset += cmd->elem_count; + } + } + + SDL_SetRenderClipRect(sdl->renderer, &saved_clip); + if (!clipping_enabled) { + SDL_SetRenderClipRect(sdl->renderer, NULL); + } + + nk_clear(&sdl->ctx); + nk_buffer_clear(&sdl->ogl.cmds); + nk_buffer_free(&vbuf); + nk_buffer_free(&ebuf); + } +} + +NK_INTERN void +nk_sdl_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) +{ + char *text; + int len; + NK_UNUSED(usr); + + /* this function returns empty string on failure, not NULL */ + text = SDL_GetClipboardText(); + NK_ASSERT(text); + + if (text[0] != '\0') { + /* FIXME: there is a bug in Nuklear that affects UTF8 clipboard handling + * "len" should be a buffer length, but due to bug it must be a glyph count + * see: https://github.com/Immediate-Mode-UI/Nuklear/pull/841 */ +#if 0 + len = nk_strlen(text); +#else + len = SDL_utf8strlen(text); +#endif + nk_textedit_paste(edit, text, len); + } + SDL_free(text); +} + +NK_INTERN void +nk_sdl_clipboard_copy(nk_handle usr, const char *text, int len) +{ + const char *ptext; + char *str; + size_t buflen; + int i; + struct nk_sdl* sdl = (struct nk_sdl*)usr.ptr; + NK_ASSERT(sdl); + if (len <= 0 || text == NULL) return; + + /* FIXME: there is a bug in Nuklear that affects UTF8 clipboard handling + * "len" is expected to be a buffer length, but due to bug it actually is a glyph count + * see: https://github.com/Immediate-Mode-UI/Nuklear/pull/841 */ +#if 0 + buflen = len + 1; + NK_UNUSED(ptext); +#else + ptext = text; + for (i = len; i > 0; i--) + (void)SDL_StepUTF8(&ptext, NULL); + buflen = (size_t)(ptext - text) + 1; +#endif + + str = sdl->allocator.alloc(sdl->allocator.userdata, 0, buflen); + if (!str) return; + SDL_strlcpy(str, text, buflen); + SDL_SetClipboardText(str); + sdl->allocator.free(sdl->allocator.userdata, str); +} + +NK_API struct nk_context* +nk_sdl_init(SDL_Window *win, SDL_Renderer *renderer, struct nk_allocator allocator) +{ + struct nk_sdl* sdl; + NK_ASSERT(win); + NK_ASSERT(renderer); + NK_ASSERT(allocator.alloc); + NK_ASSERT(allocator.free); + sdl = allocator.alloc(allocator.userdata, 0, sizeof(*sdl)); + NK_ASSERT(sdl); + SDL_zerop(sdl); + sdl->allocator.userdata = allocator.userdata; + sdl->allocator.alloc = allocator.alloc; + sdl->allocator.free = allocator.free; + sdl->win = win; + sdl->renderer = renderer; + nk_init(&sdl->ctx, &sdl->allocator, 0); + sdl->ctx.userdata = nk_handle_ptr((void*)sdl); + sdl->ctx.clip.copy = nk_sdl_clipboard_copy; + sdl->ctx.clip.paste = nk_sdl_clipboard_paste; + sdl->ctx.clip.userdata = nk_handle_ptr((void*)sdl); + nk_buffer_init(&sdl->ogl.cmds, &sdl->allocator, NK_BUFFER_DEFAULT_INITIAL_SIZE); + sdl->last_left_click = 0; + sdl->edit_was_active = false; + sdl->insert_toggle = false; + return &sdl->ctx; +} + +#ifdef NK_INCLUDE_FONT_BAKING +NK_API struct nk_font_atlas* +nk_sdl_font_stash_begin(struct nk_context* ctx) +{ + struct nk_sdl* sdl; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + nk_font_atlas_init(&sdl->atlas, &sdl->allocator); + nk_font_atlas_begin(&sdl->atlas); + return &sdl->atlas; +} +#endif + +#ifdef NK_INCLUDE_FONT_BAKING +NK_API void +nk_sdl_font_stash_end(struct nk_context* ctx) +{ + struct nk_sdl* sdl; + const void *image; int w, h; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + image = nk_font_atlas_bake(&sdl->atlas, &w, &h, NK_FONT_ATLAS_RGBA32); + NK_ASSERT(image); + nk_sdl_device_upload_atlas(&sdl->ctx, image, w, h); + nk_font_atlas_end(&sdl->atlas, nk_handle_ptr(sdl->ogl.font_tex), &sdl->ogl.tex_null); + if (sdl->atlas.default_font) { + nk_style_set_font(&sdl->ctx, &sdl->atlas.default_font->handle); + } +} +#endif + +NK_API int +nk_sdl_handle_event(struct nk_context* ctx, SDL_Event *evt) +{ + struct nk_sdl* sdl; + + NK_ASSERT(ctx); + NK_ASSERT(evt); + + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + + /* We only care about Window currently used by Nuklear */ + if (sdl->win != SDL_GetWindowFromEvent(evt)) { + return 0; + } + + switch(evt->type) + { + case SDL_EVENT_KEY_UP: /* KEYUP & KEYDOWN share same routine */ + case SDL_EVENT_KEY_DOWN: + { + int down = evt->type == SDL_EVENT_KEY_DOWN; + int ctrl_down = evt->key.mod & (SDL_KMOD_LCTRL | SDL_KMOD_RCTRL); + + /* In 99% of the time, you want to use scancodes, not real key codes, + * see: https://wiki.libsdl.org/SDL3/BestKeyboardPractices */ + switch(evt->key.scancode) + { + case SDL_SCANCODE_RSHIFT: /* RSHIFT & LSHIFT share same routine */ + case SDL_SCANCODE_LSHIFT: nk_input_key(ctx, NK_KEY_SHIFT, down); break; + case SDL_SCANCODE_DELETE: nk_input_key(ctx, NK_KEY_DEL, down); break; + case SDL_SCANCODE_RETURN: nk_input_key(ctx, NK_KEY_ENTER, down); break; + case SDL_SCANCODE_TAB: nk_input_key(ctx, NK_KEY_TAB, down); break; + case SDL_SCANCODE_BACKSPACE: nk_input_key(ctx, NK_KEY_BACKSPACE, down); break; + case SDL_SCANCODE_HOME: nk_input_key(ctx, NK_KEY_TEXT_START, down); + nk_input_key(ctx, NK_KEY_SCROLL_START, down); break; + case SDL_SCANCODE_END: nk_input_key(ctx, NK_KEY_TEXT_END, down); + nk_input_key(ctx, NK_KEY_SCROLL_END, down); break; + case SDL_SCANCODE_PAGEDOWN: nk_input_key(ctx, NK_KEY_SCROLL_DOWN, down); break; + case SDL_SCANCODE_PAGEUP: nk_input_key(ctx, NK_KEY_SCROLL_UP, down); break; + case SDL_SCANCODE_A: nk_input_key(ctx, NK_KEY_TEXT_SELECT_ALL, down && ctrl_down); break; + case SDL_SCANCODE_Z: nk_input_key(ctx, NK_KEY_TEXT_UNDO, down && ctrl_down); break; + case SDL_SCANCODE_R: nk_input_key(ctx, NK_KEY_TEXT_REDO, down && ctrl_down); break; + case SDL_SCANCODE_C: nk_input_key(ctx, NK_KEY_COPY, down && ctrl_down); break; + case SDL_SCANCODE_V: nk_input_key(ctx, NK_KEY_PASTE, down && ctrl_down); break; + case SDL_SCANCODE_X: nk_input_key(ctx, NK_KEY_CUT, down && ctrl_down); break; + case SDL_SCANCODE_B: nk_input_key(ctx, NK_KEY_TEXT_LINE_START, down && ctrl_down); break; + case SDL_SCANCODE_E: nk_input_key(ctx, NK_KEY_TEXT_LINE_END, down && ctrl_down); break; + case SDL_SCANCODE_UP: nk_input_key(ctx, NK_KEY_UP, down); break; + case SDL_SCANCODE_DOWN: nk_input_key(ctx, NK_KEY_DOWN, down); break; + case SDL_SCANCODE_ESCAPE: nk_input_key(ctx, NK_KEY_TEXT_RESET_MODE, down); break; + case SDL_SCANCODE_INSERT: + if (down) sdl->insert_toggle = !sdl->insert_toggle; + if (sdl->insert_toggle) { + nk_input_key(ctx, NK_KEY_TEXT_INSERT_MODE, down); + } else { + nk_input_key(ctx, NK_KEY_TEXT_REPLACE_MODE, down); + } + break; + case SDL_SCANCODE_LEFT: + if (ctrl_down) + nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, down); + else + nk_input_key(ctx, NK_KEY_LEFT, down); + break; + case SDL_SCANCODE_RIGHT: + if (ctrl_down) + nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, down); + else + nk_input_key(ctx, NK_KEY_RIGHT, down); + break; + default: + return 0; + } + return 1; + } + + case SDL_EVENT_MOUSE_BUTTON_UP: /* MOUSEBUTTONUP & MOUSEBUTTONDOWN share same routine */ + case SDL_EVENT_MOUSE_BUTTON_DOWN: + { + const int x = evt->button.x, y = evt->button.y; + const int down = evt->button.down; + const double dt = (double)(evt->button.timestamp - sdl->last_left_click) / 1000000000.0; + switch(evt->button.button) + { + case SDL_BUTTON_LEFT: + nk_input_button(ctx, NK_BUTTON_LEFT, x, y, down); + nk_input_button(ctx, NK_BUTTON_DOUBLE, x, y, + down && dt > NK_SDL_DOUBLE_CLICK_LO && dt < NK_SDL_DOUBLE_CLICK_HI); + sdl->last_left_click = evt->button.timestamp; + 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; + default: + return 0; + } + } + return 1; + + case SDL_EVENT_MOUSE_MOTION: + ctx->input.mouse.pos.x = evt->motion.x; + ctx->input.mouse.pos.y = evt->motion.y; + ctx->input.mouse.delta.x = ctx->input.mouse.pos.x - ctx->input.mouse.prev.x; + ctx->input.mouse.delta.y = ctx->input.mouse.pos.y - ctx->input.mouse.prev.y; + return 1; + + case SDL_EVENT_TEXT_INPUT: + { + nk_glyph glyph; + nk_size len; + NK_ASSERT(evt->text.text); + len = SDL_strlen(evt->text.text); + NK_ASSERT(len <= NK_UTF_SIZE); + NK_MEMCPY(glyph, evt->text.text, len); + nk_input_glyph(ctx, glyph); + } + return 1; + + case SDL_EVENT_MOUSE_WHEEL: + nk_input_scroll(ctx, nk_vec2(evt->wheel.x, evt->wheel.y)); + return 1; + } + return 0; +} + +NK_API +void nk_sdl_shutdown(struct nk_context* ctx) +{ + struct nk_sdl* sdl; + NK_ASSERT(ctx); + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + +#ifdef NK_INCLUDE_FONT_BAKING + if (sdl->atlas.font_num > 0) + nk_font_atlas_clear(&sdl->atlas); +#endif + + nk_buffer_free(&sdl->ogl.cmds); + + if (sdl->ogl.font_tex != NULL) { + SDL_DestroyTexture(sdl->ogl.font_tex); + sdl->ogl.font_tex = NULL; + } + + nk_free(ctx); + sdl->allocator.free(sdl->allocator.userdata, sdl->debug_font); + sdl->allocator.free(sdl->allocator.userdata, sdl); +} + +/* Debug Font Width/Height of internal texture atlas + * This is a result of: ceil(sqrt('~' - ' ')) + * There is a sanity check for this value in nk_sdl_style_set_debug_font */ +#define NK_SDL_DFWH (10) + +NK_INTERN float +nk_sdl_query_debug_font_width(nk_handle handle, float height, + const char *text, int len) +{ + NK_UNUSED(handle); + return nk_utf_len(text, len) * height; +} + +NK_INTERN void +nk_sdl_query_debug_font_glypth(nk_handle handle, float height, + struct nk_user_font_glyph *glyph, + nk_rune codepoint, nk_rune next_codepoint) +{ + char ascii; + int idx, x, y; + NK_UNUSED(next_codepoint); + NK_UNUSED(handle); + + /* replace non-ASCII characters with question mark */ + ascii = (codepoint < (nk_rune)' ' || codepoint > (nk_rune)'~') + ? '?' : (char)codepoint; + NK_ASSERT(ascii >= ' ' && ascii <= '~'); + + idx = (int)(ascii - ' '); + x = idx / NK_SDL_DFWH; + y = idx % NK_SDL_DFWH; + NK_ASSERT(x >= 0 && x < NK_SDL_DFWH); + NK_ASSERT(y >= 0 && y < NK_SDL_DFWH); + + glyph->height = height; + glyph->width = height; + glyph->xadvance = height; + glyph->uv[0].x = (float)(x + 0) / NK_SDL_DFWH; + glyph->uv[0].y = (float)(y + 0) / NK_SDL_DFWH; + glyph->uv[1].x = (float)(x + 1) / NK_SDL_DFWH; + glyph->uv[1].y = (float)(y + 1) / NK_SDL_DFWH; + glyph->offset.x = 0.0f; + glyph->offset.y = 0.0f; +} + +NK_API void +nk_sdl_style_set_debug_font(struct nk_context* ctx) +{ + struct nk_user_font* font; + struct nk_sdl* sdl; + SDL_Surface *surface; + SDL_Renderer *renderer; + char buf[2]; + int x, y; + bool success; + NK_ASSERT(ctx); + + sdl = (struct nk_sdl*)ctx->userdata.ptr; + NK_ASSERT(sdl); + + if (sdl->debug_font) { + sdl->allocator.free(sdl->allocator.userdata, sdl->debug_font); + sdl->debug_font = 0; + } + + /* sanity check: formal proof of NK_SDL_DFWH value (which is 10) */ + NK_ASSERT(SDL_ceil(SDL_sqrt('~' - ' ')) == NK_SDL_DFWH); + + /* We use another Software Renderer just to make sure + * that we won't mutate any state in the main Renderer. */ + surface = SDL_CreateSurface( + NK_SDL_DFWH * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE, + NK_SDL_DFWH * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE, + SDL_PIXELFORMAT_RGBA32); + NK_ASSERT(surface); + renderer = SDL_CreateSoftwareRenderer(surface); + NK_ASSERT(renderer); + success = SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + NK_ASSERT(success); + + /* SPACE is the first printable ASCII character */ + NK_MEMCPY(buf, " ", sizeof(buf)); + for (x = 0; x < NK_SDL_DFWH; x++) + { + for (y = 0; y < NK_SDL_DFWH; y++) + { + success = SDL_RenderDebugText( + renderer, + (float)(x * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE), + (float)(y * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE), + buf); + NK_ASSERT(success); + buf[0]++; + + /* TILDE is the last printable ASCII character */ + if (buf[0] > '~') + break; + } + } + success = SDL_RenderPresent(renderer); + NK_ASSERT(success); + + font = sdl->allocator.alloc(sdl->allocator.userdata, 0, sizeof(*font)); + NK_ASSERT(font); + font->userdata.ptr = sdl; + font->height = SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + font->width = &nk_sdl_query_debug_font_width; + font->query = &nk_sdl_query_debug_font_glypth; + + /* HACK: nk_sdl_device_upload_atlas turns pixels into SDL_Texture + * and sets said Texture into sdl->ogl.font_tex + * then nk_sdl_render expects same Texture at font->texture */ + nk_sdl_device_upload_atlas(ctx, surface->pixels, surface->w, surface->h); + font->texture.ptr = sdl->ogl.font_tex; + + sdl->debug_font = font; + nk_style_set_font(ctx, font); + + SDL_DestroyRenderer(renderer); + SDL_DestroySurface(surface); +} + +#endif /* NK_SDL3_RENDERER_IMPLEMENTATION_ONCE */ +#endif /* NK_SDL3_RENDERER_IMPLEMENTATION */ + diff --git a/nuklear.h b/nuklear.h index a1fa9d1c..1b13281f 100644 --- a/nuklear.h +++ b/nuklear.h @@ -29073,6 +29073,7 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant win->property.name = hash; win->property.select_start = *select_begin; win->property.select_end = *select_end; + win->edit.active = nk_true; if (*state == NK_PROPERTY_DRAG) { ctx->input.mouse.grab = nk_true; ctx->input.mouse.grabbed = nk_true; @@ -29088,6 +29089,7 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant win->property.select_start = 0; win->property.select_end = 0; win->property.active = 0; + win->edit.active = nk_false; } } NK_API void @@ -30729,6 +30731,8 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2025/11/15 (4.13.0) - Fix: nk_property not updating 'win->edit.active' +/// Add new updated demo: sdl3_renderer /// - 2025/10/08 (4.12.8) - Fix nk_widget_text to use NK_TEXT_ALIGN_LEFT by default, /// instead of silently failing when no x-axis alignment is provided, /// and refactor this function to keep the code style consistent diff --git a/src/CHANGELOG b/src/CHANGELOG index fa81dc46..8de53d80 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -7,6 +7,8 @@ /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2025/11/15 (4.13.0) - Fix: nk_property not updating 'win->edit.active' +/// Add new updated demo: sdl3_renderer /// - 2025/10/08 (4.12.8) - Fix nk_widget_text to use NK_TEXT_ALIGN_LEFT by default, /// instead of silently failing when no x-axis alignment is provided, /// and refactor this function to keep the code style consistent diff --git a/src/nuklear_property.c b/src/nuklear_property.c index 68b47ba7..668b78c8 100644 --- a/src/nuklear_property.c +++ b/src/nuklear_property.c @@ -405,6 +405,7 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant win->property.name = hash; win->property.select_start = *select_begin; win->property.select_end = *select_end; + win->edit.active = nk_true; if (*state == NK_PROPERTY_DRAG) { ctx->input.mouse.grab = nk_true; ctx->input.mouse.grabbed = nk_true; @@ -420,6 +421,7 @@ nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant win->property.select_start = 0; win->property.select_end = 0; win->property.active = 0; + win->edit.active = nk_false; } } NK_API void