Skip to content

Commit 8868050

Browse files
authored
Merge pull request #801 from rswinkle/fix_text_input_behavior
Fix text input behavior
2 parents e5e4270 + 912a52c commit 8868050

File tree

7 files changed

+78
-44
lines changed

7 files changed

+78
-44
lines changed

clib.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nuklear",
3-
"version": "4.12.6",
3+
"version": "4.12.7",
44
"repo": "Immediate-Mode-UI/Nuklear",
55
"description": "A small ANSI C gui toolkit",
66
"keywords": ["gl", "ui", "toolkit"],

demo/glfw_opengl3/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
BIN = demo
33

44
# Flags
5-
CFLAGS += -std=c89 -Wall -Wextra -pedantic
5+
CFLAGS += -g -std=c89 -Wall -Wextra -pedantic
66

77
SRC = main.c
88
OBJ = $(SRC:.c=.o)

nuklear.h

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occu
109109
NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame.
110110
NK_UINT_DRAW_INDEX | Defining this will set the size of vertex index elements when using NK_VERTEX_BUFFER_OUTPUT to 32bit instead of the default of 16bit
111111
NK_KEYSTATE_BASED_INPUT | Define this if your backend uses key state for each frame rather than key press/release events
112+
NK_IS_WORD_BOUNDARY(c) | Define this to a function macro that takes a single nk_rune (nk_uint) and returns true if it's a word separator. If not defined, uses the default definition (see nk_is_word_boundary())
112113

113114
!!! WARNING
114115
The following flags will pull in the standard C library:
@@ -27054,34 +27055,44 @@ nk_is_word_boundary( struct nk_text_edit *state, int idx)
2705427055
{
2705527056
int len;
2705627057
nk_rune c;
27057-
if (idx <= 0) return 1;
27058+
if (idx < 0) return 1;
2705827059
if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1;
27059-
return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' ||
27060-
c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' ||
27061-
c == '|');
27060+
#ifndef NK_IS_WORD_BOUNDARY
27061+
return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
27062+
c == '\v' || c == 0x3000);
27063+
#else
27064+
return NK_IS_WORD_BOUNDARY(c);
27065+
#endif
2706227066
}
2706327067
NK_INTERN int
2706427068
nk_textedit_move_to_word_previous(struct nk_text_edit *state)
2706527069
{
2706627070
int c = state->cursor - 1;
27067-
while( c >= 0 && !nk_is_word_boundary(state, c))
27068-
--c;
27069-
27070-
if( c < 0 )
27071-
c = 0;
27071+
if (c > 0) {
27072+
if (nk_is_word_boundary(state, c)) {
27073+
while (c > 0 && nk_is_word_boundary(state, --c));
27074+
}
27075+
while (!nk_is_word_boundary(state, --c));
27076+
c++;
27077+
} else {
27078+
return 0;
27079+
}
2707227080

2707327081
return c;
2707427082
}
2707527083
NK_INTERN int
2707627084
nk_textedit_move_to_word_next(struct nk_text_edit *state)
2707727085
{
2707827086
const int len = state->string.len;
27079-
int c = state->cursor+1;
27080-
while( c < len && !nk_is_word_boundary(state, c))
27081-
++c;
27082-
27083-
if( c > len )
27084-
c = len;
27087+
int c = state->cursor;
27088+
if (c < len) {
27089+
if (!nk_is_word_boundary(state, c)) {
27090+
while (c < len && !nk_is_word_boundary(state, ++c));
27091+
}
27092+
while (c < len && nk_is_word_boundary(state, ++c));
27093+
} else {
27094+
return len;
27095+
}
2708527096

2708627097
return c;
2708727098
}
@@ -27266,7 +27277,7 @@ nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod,
2726627277
case NK_KEY_TEXT_WORD_LEFT:
2726727278
if (shift_mod) {
2726827279
if( !NK_TEXT_HAS_SELECTION( state ) )
27269-
nk_textedit_prep_selection_at_cursor(state);
27280+
nk_textedit_prep_selection_at_cursor(state);
2727027281
state->cursor = nk_textedit_move_to_word_previous(state);
2727127282
state->select_end = state->cursor;
2727227283
nk_textedit_clamp(state );
@@ -27995,7 +28006,6 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
2799528006

2799628007
/* update edit state */
2799728008
prev_state = (char)edit->active;
27998-
is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds);
2799928009
if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) {
2800028010
edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y,
2800128011
bounds.x, bounds.y, bounds.w, bounds.h);
@@ -28287,10 +28297,12 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
2828728297
} else edit->scrollbar.x = 0;
2828828298

2828928299
if (flags & NK_EDIT_MULTILINE) {
28290-
/* vertical scroll */
28300+
/* vertical scroll: like horizontal, it only adjusts if the
28301+
* cursor leaves the visible area, and then only just enough
28302+
* to keep it visible */
2829128303
if (cursor_pos.y < edit->scrollbar.y)
28292-
edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height);
28293-
if (cursor_pos.y >= edit->scrollbar.y + row_height)
28304+
edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y);
28305+
if (cursor_pos.y > edit->scrollbar.y + area.h - row_height)
2829428306
edit->scrollbar.y = edit->scrollbar.y + row_height;
2829528307
} else edit->scrollbar.y = 0;
2829628308
}
@@ -28313,9 +28325,13 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
2831328325
scroll_step = scroll.h * 0.10f;
2831428326
scroll_inc = scroll.h * 0.01f;
2831528327
scroll_target = text_size.y;
28316-
edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0,
28328+
edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, is_hovered,
2831728329
scroll_offset, scroll_target, scroll_step, scroll_inc,
2831828330
&style->scrollbar, in, font);
28331+
/* Eat mouse scroll if we're active */
28332+
if (is_hovered && in->mouse.scroll_delta.y) {
28333+
in->mouse.scroll_delta.y = 0;
28334+
}
2831928335
}
2832028336
}
2832128337

@@ -30700,6 +30716,7 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args)
3070030716
/// - [y]: Minor version with non-breaking API and library changes
3070130717
/// - [z]: Patch version with no direct changes to the API
3070230718
///
30719+
/// - 2025/04/06 (4.12.7) - Fix text input navigation and mouse scrolling
3070330720
/// - 2025/03/29 (4.12.6) - Fix unitialized data in nk_input_char
3070430721
/// - 2025/03/05 (4.12.5) - Fix scrolling knob also scrolling parent window, remove dead code
3070530722
/// - 2024/12/11 (4.12.4) - Fix array subscript [0, 0] is outside array bounds of ‘char[1]’

src/CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/// - [y]: Minor version with non-breaking API and library changes
88
/// - [z]: Patch version with no direct changes to the API
99
///
10+
/// - 2025/04/06 (4.12.7) - Fix text input navigation and mouse scrolling
1011
/// - 2025/03/29 (4.12.6) - Fix unitialized data in nk_input_char
1112
/// - 2025/03/05 (4.12.5) - Fix scrolling knob also scrolling parent window, remove dead code
1213
/// - 2024/12/11 (4.12.4) - Fix array subscript [0, 0] is outside array bounds of ‘char[1]’

src/HEADER.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occu
108108
NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame.
109109
NK_UINT_DRAW_INDEX | Defining this will set the size of vertex index elements when using NK_VERTEX_BUFFER_OUTPUT to 32bit instead of the default of 16bit
110110
NK_KEYSTATE_BASED_INPUT | Define this if your backend uses key state for each frame rather than key press/release events
111+
NK_IS_WORD_BOUNDARY(c) | Define this to a function macro that takes a single nk_rune (nk_uint) and returns true if it's a word separator. If not defined, uses the default definition (see nk_is_word_boundary())
111112

112113
!!! WARNING
113114
The following flags will pull in the standard C library:

src/nuklear_edit.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
188188

189189
/* update edit state */
190190
prev_state = (char)edit->active;
191-
is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds);
192191
if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) {
193192
edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y,
194193
bounds.x, bounds.y, bounds.w, bounds.h);
@@ -480,10 +479,12 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
480479
} else edit->scrollbar.x = 0;
481480

482481
if (flags & NK_EDIT_MULTILINE) {
483-
/* vertical scroll */
482+
/* vertical scroll: like horizontal, it only adjusts if the
483+
* cursor leaves the visible area, and then only just enough
484+
* to keep it visible */
484485
if (cursor_pos.y < edit->scrollbar.y)
485-
edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height);
486-
if (cursor_pos.y >= edit->scrollbar.y + row_height)
486+
edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y);
487+
if (cursor_pos.y > edit->scrollbar.y + area.h - row_height)
487488
edit->scrollbar.y = edit->scrollbar.y + row_height;
488489
} else edit->scrollbar.y = 0;
489490
}
@@ -506,9 +507,13 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
506507
scroll_step = scroll.h * 0.10f;
507508
scroll_inc = scroll.h * 0.01f;
508509
scroll_target = text_size.y;
509-
edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0,
510+
edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, is_hovered,
510511
scroll_offset, scroll_target, scroll_step, scroll_inc,
511512
&style->scrollbar, in, font);
513+
/* Eat mouse scroll if we're active */
514+
if (is_hovered && in->mouse.scroll_delta.y) {
515+
in->mouse.scroll_delta.y = 0;
516+
}
512517
}
513518
}
514519

src/nuklear_text_editor.c

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -277,34 +277,44 @@ nk_is_word_boundary( struct nk_text_edit *state, int idx)
277277
{
278278
int len;
279279
nk_rune c;
280-
if (idx <= 0) return 1;
280+
if (idx < 0) return 1;
281281
if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1;
282-
return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' ||
283-
c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' ||
284-
c == '|');
282+
#ifndef NK_IS_WORD_BOUNDARY
283+
return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
284+
c == '\v' || c == 0x3000);
285+
#else
286+
return NK_IS_WORD_BOUNDARY(c);
287+
#endif
285288
}
286289
NK_INTERN int
287290
nk_textedit_move_to_word_previous(struct nk_text_edit *state)
288291
{
289292
int c = state->cursor - 1;
290-
while( c >= 0 && !nk_is_word_boundary(state, c))
291-
--c;
292-
293-
if( c < 0 )
294-
c = 0;
293+
if (c > 0) {
294+
if (nk_is_word_boundary(state, c)) {
295+
while (c > 0 && nk_is_word_boundary(state, --c));
296+
}
297+
while (!nk_is_word_boundary(state, --c));
298+
c++;
299+
} else {
300+
return 0;
301+
}
295302

296303
return c;
297304
}
298305
NK_INTERN int
299306
nk_textedit_move_to_word_next(struct nk_text_edit *state)
300307
{
301308
const int len = state->string.len;
302-
int c = state->cursor+1;
303-
while( c < len && !nk_is_word_boundary(state, c))
304-
++c;
305-
306-
if( c > len )
307-
c = len;
309+
int c = state->cursor;
310+
if (c < len) {
311+
if (!nk_is_word_boundary(state, c)) {
312+
while (c < len && !nk_is_word_boundary(state, ++c));
313+
}
314+
while (c < len && nk_is_word_boundary(state, ++c));
315+
} else {
316+
return len;
317+
}
308318

309319
return c;
310320
}
@@ -489,7 +499,7 @@ nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod,
489499
case NK_KEY_TEXT_WORD_LEFT:
490500
if (shift_mod) {
491501
if( !NK_TEXT_HAS_SELECTION( state ) )
492-
nk_textedit_prep_selection_at_cursor(state);
502+
nk_textedit_prep_selection_at_cursor(state);
493503
state->cursor = nk_textedit_move_to_word_previous(state);
494504
state->select_end = state->cursor;
495505
nk_textedit_clamp(state );

0 commit comments

Comments
 (0)