diff --git a/clib.json b/clib.json index e704fda7..b872eeb6 100644 --- a/clib.json +++ b/clib.json @@ -1,6 +1,6 @@ { "name": "nuklear", - "version": "4.12.8", + "version": "4.13.0", "repo": "Immediate-Mode-UI/Nuklear", "description": "A small ANSI C gui toolkit", "keywords": ["gl", "ui", "toolkit"], diff --git a/nuklear.h b/nuklear.h index a1fa9d1c..60ba5c05 100644 --- a/nuklear.h +++ b/nuklear.h @@ -4917,6 +4917,11 @@ struct nk_keyboard { struct nk_key keys[NK_KEY_MAX]; char text[NK_INPUT_MAX]; int text_len; + + /*Text keyboard request system*/ + nk_bool text_want; /*When true, text input is actively requested*/ + struct nk_rect text_area; /*Input area (valid only when text_want == true)*/ + struct nk_rect text_cursor; /*Cursor position (valid only when text_want == true)*/ }; struct nk_input { @@ -4941,6 +4946,10 @@ NK_API nk_bool nk_input_is_mouse_released(const struct nk_input*, enum nk_button NK_API nk_bool nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_released(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_down(const struct nk_input*, enum nk_keys); +/* This is a utility function that should be called inside a widget + * to mark text keyboard for opening. + * Returns true only on the first request each frame */ +NK_API nk_bool nk_input_want_text_keyboard(struct nk_input*); /* =============================================================== * @@ -18199,6 +18208,10 @@ nk_input_begin(struct nk_context *ctx) for (i = 0; i < NK_BUTTON_MAX; ++i) in->mouse.buttons[i].clicked = 0; + in->keyboard.text_want = nk_false; + in->keyboard.text_area = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); + in->keyboard.text_cursor = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); + in->keyboard.text_len = 0; in->mouse.scroll_delta = nk_vec2(0,0); in->mouse.prev.x = in->mouse.pos.x; @@ -18469,7 +18482,15 @@ nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) if (k->down) return nk_true; return nk_false; } - +NK_API nk_bool +nk_input_want_text_keyboard(struct nk_input* i) +{ + nk_bool ret; + NK_ASSERT(i); + ret = !i->keyboard.text_want; + i->keyboard.text_want = nk_true; + return ret; +} @@ -27997,6 +28018,9 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, char cursor_follow = 0; struct nk_rect old_clip; struct nk_rect clip; + /*Variables for text keyboard(kb) support*/ + nk_bool kb_text_want = nk_false; + struct nk_rect kb_text_cursor = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); NK_ASSERT(state); NK_ASSERT(out); @@ -28082,13 +28106,17 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, cursor_follow = nk_true; } } - if (old_mode != edit->mode) { + /* If mode changes and text keys are pressed in the same frame, + * mode change takes priority (may transition to VIEW mode) */ + if (old_mode != edit->mode) in->keyboard.text_len = 0; - }} + else if (edit->mode == NK_TEXT_EDIT_MODE_INSERT || edit->mode == NK_TEXT_EDIT_MODE_REPLACE) + kb_text_want = nk_input_want_text_keyboard(in); + } /* text input */ edit->filter = filter; - if (in->keyboard.text_len) { + if (kb_text_want && in->keyboard.text_len) { nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); cursor_follow = nk_true; in->keyboard.text_len = 0; @@ -28407,12 +28435,23 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, row_height, font, background_color, text_color, nk_false); } if (edit->select_start != edit->select_end) { + int glyph_len; + nk_rune unicode; + /* draw selected text */ NK_ASSERT(select_begin_ptr); if (!select_end_ptr) { const char *begin = nk_str_get_const(&edit->string); select_end_ptr = begin + nk_str_len_char(&edit->string); } + + glyph_len = nk_utf_decode(select_begin_ptr, &unicode, (int)(select_end_ptr - select_begin_ptr)); + /* In this case, we set the cursor to the first selected character */ + kb_text_cursor = nk_rect( + area.x + selection_offset_start.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + font->width(font->userdata, font->height, select_begin_ptr, glyph_len), row_height); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y + selection_offset_start.y - edit->scrollbar.y, @@ -28449,6 +28488,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; cursor.y -= edit->scrollbar.y; + kb_text_cursor = cursor; nk_fill_rect(out, cursor, 0, cursor_color); } else { /* draw cursor inside text */ @@ -28468,6 +28508,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, txt.padding = nk_vec2(0,0); txt.background = cursor_color;; txt.text = cursor_text_color; + kb_text_cursor = label; nk_fill_rect(out, label, 0, cursor_color); nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); } @@ -28504,6 +28545,11 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, background_color, text_color, nk_false); } nk_push_scissor(out, old_clip);} + if (kb_text_want) { + NK_ASSERT(in); + in->keyboard.text_cursor = kb_text_cursor; + in->keyboard.text_area = clip; + } return ret; } NK_API void @@ -30729,6 +30775,9 @@ 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/13 (4.13.0) - Introduced minimal text keyboard request API (nk_input_want_text_keyboard) +/// with support for text input area and cursor position. +/// This functionality is required for SDL_SetTextInputRect /// - 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..1ab8c934 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -7,6 +7,9 @@ /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2025/11/13 (4.13.0) - Introduced minimal text keyboard request API (nk_input_want_text_keyboard) +/// with support for text input area and cursor position. +/// This functionality is required for SDL_SetTextInputRect /// - 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.h b/src/nuklear.h index 9362e816..028cd343 100644 --- a/src/nuklear.h +++ b/src/nuklear.h @@ -4694,6 +4694,11 @@ struct nk_keyboard { struct nk_key keys[NK_KEY_MAX]; char text[NK_INPUT_MAX]; int text_len; + + /*Text keyboard request system*/ + nk_bool text_want; /*When true, text input is actively requested*/ + struct nk_rect text_area; /*Input area (valid only when text_want == true)*/ + struct nk_rect text_cursor; /*Cursor position (valid only when text_want == true)*/ }; struct nk_input { @@ -4718,6 +4723,10 @@ NK_API nk_bool nk_input_is_mouse_released(const struct nk_input*, enum nk_button NK_API nk_bool nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_released(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_down(const struct nk_input*, enum nk_keys); +/* This is a utility function that should be called inside a widget + * to mark text keyboard for opening. + * Returns true only on the first request each frame */ +NK_API nk_bool nk_input_want_text_keyboard(struct nk_input*); /* =============================================================== * diff --git a/src/nuklear_edit.c b/src/nuklear_edit.c index ae5daed0..48f9e84d 100644 --- a/src/nuklear_edit.c +++ b/src/nuklear_edit.c @@ -166,6 +166,9 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, char cursor_follow = 0; struct nk_rect old_clip; struct nk_rect clip; + /*Variables for text keyboard(kb) support*/ + nk_bool kb_text_want = nk_false; + struct nk_rect kb_text_cursor = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); NK_ASSERT(state); NK_ASSERT(out); @@ -251,13 +254,17 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, cursor_follow = nk_true; } } - if (old_mode != edit->mode) { + /* If mode changes and text keys are pressed in the same frame, + * mode change takes priority (may transition to VIEW mode) */ + if (old_mode != edit->mode) in->keyboard.text_len = 0; - }} + else if (edit->mode == NK_TEXT_EDIT_MODE_INSERT || edit->mode == NK_TEXT_EDIT_MODE_REPLACE) + kb_text_want = nk_input_want_text_keyboard(in); + } /* text input */ edit->filter = filter; - if (in->keyboard.text_len) { + if (kb_text_want && in->keyboard.text_len) { nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); cursor_follow = nk_true; in->keyboard.text_len = 0; @@ -576,12 +583,23 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, row_height, font, background_color, text_color, nk_false); } if (edit->select_start != edit->select_end) { + int glyph_len; + nk_rune unicode; + /* draw selected text */ NK_ASSERT(select_begin_ptr); if (!select_end_ptr) { const char *begin = nk_str_get_const(&edit->string); select_end_ptr = begin + nk_str_len_char(&edit->string); } + + glyph_len = nk_utf_decode(select_begin_ptr, &unicode, (int)(select_end_ptr - select_begin_ptr)); + /* In this case, we set the cursor to the first selected character */ + kb_text_cursor = nk_rect( + area.x + selection_offset_start.x - edit->scrollbar.x, + area.y + selection_offset_start.y - edit->scrollbar.y, + font->width(font->userdata, font->height, select_begin_ptr, glyph_len), row_height); + nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y + selection_offset_start.y - edit->scrollbar.y, @@ -618,6 +636,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; cursor.y -= edit->scrollbar.y; + kb_text_cursor = cursor; nk_fill_rect(out, cursor, 0, cursor_color); } else { /* draw cursor inside text */ @@ -637,6 +656,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, txt.padding = nk_vec2(0,0); txt.background = cursor_color;; txt.text = cursor_text_color; + kb_text_cursor = label; nk_fill_rect(out, label, 0, cursor_color); nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); } @@ -673,6 +693,11 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out, background_color, text_color, nk_false); } nk_push_scissor(out, old_clip);} + if (kb_text_want) { + NK_ASSERT(in); + in->keyboard.text_cursor = kb_text_cursor; + in->keyboard.text_area = clip; + } return ret; } NK_API void diff --git a/src/nuklear_input.c b/src/nuklear_input.c index 9480a522..7a2a11b0 100644 --- a/src/nuklear_input.c +++ b/src/nuklear_input.c @@ -17,6 +17,10 @@ nk_input_begin(struct nk_context *ctx) for (i = 0; i < NK_BUTTON_MAX; ++i) in->mouse.buttons[i].clicked = 0; + in->keyboard.text_want = nk_false; + in->keyboard.text_area = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); + in->keyboard.text_cursor = nk_rect(0.0f, 0.0f, 0.0f, 0.0f); + in->keyboard.text_len = 0; in->mouse.scroll_delta = nk_vec2(0,0); in->mouse.prev.x = in->mouse.pos.x; @@ -287,4 +291,12 @@ nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) if (k->down) return nk_true; return nk_false; } - +NK_API nk_bool +nk_input_want_text_keyboard(struct nk_input* i) +{ + nk_bool ret; + NK_ASSERT(i); + ret = !i->keyboard.text_want; + i->keyboard.text_want = nk_true; + return ret; +}