Skip to content
This repository has been archived by the owner on Dec 2, 2019. It is now read-only.

Bug fixes and add feature: text edit wrap #819

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51,310 changes: 25,735 additions & 25,575 deletions nuklear.h

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion src/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
/// - [yy]: Minor version with non-breaking API and library changes
/// - [zz]: Bug fix version with no direct changes to API
///
/// - 2019/03/14 (4.01.1) - Add feature: Multiline edit widget with automatic text wrap.
/// Fix some bugs too.
/// - 2018/10/31 (4.00.2) - Added NK_KEYSTATE_BASED_INPUT to "fix" state based backends
like GLFW without breaking key repeat behavior on event based.
/// like GLFW without breaking key repeat behavior on event based.
/// - 2018/04/01 (4.00.1) - Fixed calling `nk_convert` multiple time per single frame
/// - 2018/04/01 (4.00.0) - BREAKING CHANGE: nk_draw_list_clear no longer tries to
/// clear provided buffers. So make sure to either free
Expand Down
1 change: 1 addition & 0 deletions src/nuklear.h
Original file line number Diff line number Diff line change
Expand Up @@ -3160,6 +3160,7 @@ enum nk_edit_events {
NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter);
NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter);
NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter);
NK_API nk_flags nk_edit_buffer_wrap(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter);
NK_API void nk_edit_focus(struct nk_context*, nk_flags flags);
NK_API void nk_edit_unfocus(struct nk_context*);
/* =============================================================================
Expand Down
150 changes: 147 additions & 3 deletions src/nuklear_edit.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ nk_edit_draw_text(struct nk_command_buffer *out,
if (!glyph_len) return;
while ((text_len < byte_len) && glyph_len)
{
if (unicode == '\n') {
if (unicode == '\n' || unicode == '\v') {
/* new line separator so draw previous line */
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For edit auto wrap feature. Consider a vertical tab (\v) as a line break too.

struct nk_rect label;
label.y = pos_y + line_offset;
Expand Down Expand Up @@ -332,6 +332,14 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
} else nk_draw_image(out, bounds, &background->data.image, nk_white);}

area.w = NK_MAX(0, area.w - style->cursor_size);
if (flags & NK_EDIT_MULTILINE){
area.y += row_height/2.0f;
area.h -= row_height;

/* calculate clipping rectangle */
old_clip = out->clip;
nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h);
}
if (edit->active)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix: text rendering out of clip box.

{
int total_lines = 1;
Expand Down Expand Up @@ -421,7 +429,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
selection_offset_end.x = row_size.x;
select_end_ptr = text + text_len;
}
if (unicode == '\n') {
if (unicode == '\n' || unicode == '\v') {
text_size.x = NK_MAX(text_size.x, line_width);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For edit auto wrap feature. Consider a vertical tab (\v) as a line break too.

total_lines++;
line_width = 0;
Expand Down Expand Up @@ -468,7 +476,7 @@ nk_do_edit(nk_flags *state, struct nk_command_buffer *out,
/* vertical scroll */
if (cursor_pos.y < edit->scrollbar.y)
edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height);
if (cursor_pos.y >= edit->scrollbar.y + area.h)
if (cursor_pos.y >= edit->scrollbar.y + area.h - row_height)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix: text rendering out of clip box.

edit->scrollbar.y = edit->scrollbar.y + row_height;
} else edit->scrollbar.y = 0;
}
Expand Down Expand Up @@ -794,6 +802,142 @@ nk_edit_buffer(struct nk_context *ctx, nk_flags flags,
} return ret_flags;
}
NK_API nk_flags
nk_edit_buffer_wrap(struct nk_context *ctx, nk_flags flags,
struct nk_text_edit *edit, nk_plugin_filter filter)
{
struct nk_window *win;
struct nk_style *style;
struct nk_input *in;

enum nk_widget_layout_states state;
struct nk_rect bounds;

nk_flags ret_flags = 0;
unsigned char prev_state;
nk_hash hash;

/* make sure correct values */
NK_ASSERT(ctx);
NK_ASSERT(edit);
NK_ASSERT(ctx->current);
NK_ASSERT(ctx->current->layout);
if (!ctx || !ctx->current || !ctx->current->layout)
return 0;

win = ctx->current;
style = &ctx->style;
state = nk_widget(&bounds, ctx);
if (!state) return state;

float wrap_w = bounds.w - (2.0f * style->edit.padding.x + 2 * style->edit.border) - 2 * style->font->height;
if (flags & NK_EDIT_MULTILINE)
wrap_w = NK_MAX(0, wrap_w - style->edit.scrollbar_size.x);

/* ---------------- wrap text in text edit widget ----------------------*/
char *text = nk_str_get(&edit->string);
int byte_len = nk_str_len_char(&edit->string);
double w = 0, line_w = 0;
if (text){
int text_len = 0, last_spc = 0;
int glyph_len = 0;
nk_rune unicode = 0;

glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len);
while ((text_len < byte_len) && glyph_len){ /* sweep the string */
glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len);
/* find the good point for break a line (in space caracter) */
if ((unicode == ' ') || (unicode == '\t')){
last_spc = text_len;
glyph_len = 1;
}
/* convert line break to space temporaly, until find a good point to break */
else if (unicode == '\v'){
nk_str_delete_chars(&edit->string, text_len, 1);
if (edit->cursor >= text_len) edit->cursor--;
continue;
}
/* consider a \n caracter as a paragraph break */
else if (unicode == '\n'){
/* reset the line parameters */
last_spc = 0;
line_w = 0;
glyph_len = 1;
}

/* get graphical width of current glyph */
w = style->font->width(style->font->userdata, style->font->height, text+text_len, glyph_len);
/* update width of current line */
line_w += w;

if (line_w > wrap_w){ /* verify if current line width exceeds the drawing area */
byte_len = nk_str_len_char(&edit->string);
/* consider a tolerance of two glyphs to avoid repetitive breaks */
int tolerance = 0;
char *near_line = strpbrk(text + text_len, "\v\n");
if (near_line) tolerance = near_line - text - text_len; /* tolerance until the next break */
else tolerance = byte_len - text_len; /* tolerance until the string end */
if (tolerance > 3){ /* need to break */
if (last_spc){ /* if has a good point for break, use it */
nk_str_insert_text_char(&edit->string, last_spc + 1, "\v", 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vurtun does something like this happen also on other places in the library?

IMHO we shall not allow this as we can't guarantee, that we'll put the string to it's original state once edited internally by Nuklear (because it's not distinguishable from a user made edition). It seems also not intuitive for the programmer to edit the string internally when the function itself is "just for visualisation" (this edition of "inserting \v" is not done by user).

I can though understand, that such internal editions do make the code simpler. For that purpose we could consider something like a string view providing this "metainformation" about internally inserted/deleted characters, but not editing the underlying string unless it's a user made edition.

if (edit->cursor >= last_spc) edit->cursor++;
/* start the current line in break point */
text_len = last_spc + 1;
glyph_len = 1;
last_spc = 0;
line_w = 0;
}
else{
nk_str_insert_text_char(&edit->string, text_len, "\v", 1);
if (edit->cursor >= text_len) edit->cursor++;
line_w = 0;
}
}
}

text_len += glyph_len;
text = nk_str_get(&edit->string);
byte_len = nk_str_len_char(&edit->string);
}
}
/*--------------------------------------------------------------*/

in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input;

/* check if edit is currently hot item */
hash = win->edit.seq++;
if (win->edit.active && hash == win->edit.name) {
if (flags & NK_EDIT_NO_CURSOR)
edit->cursor = edit->string.len;
if (!(flags & NK_EDIT_SELECTABLE)) {
edit->select_start = edit->cursor;
edit->select_end = edit->cursor;
}
if (flags & NK_EDIT_CLIPBOARD)
edit->clip = ctx->clip;
edit->active = (unsigned char)win->edit.active;
} else edit->active = nk_false;
edit->mode = win->edit.mode;


filter = (!filter) ? nk_filter_default: filter;
prev_state = (unsigned char)edit->active;
in = (flags & NK_EDIT_READ_ONLY) ? 0: in;
ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags|NK_EDIT_NO_HORIZONTAL_SCROLL,
filter, edit, &style->edit, in, style->font);


if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER)
ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT];
if (edit->active && prev_state != edit->active) {
/* current edit is now hot */
win->edit.active = nk_true;
win->edit.name = hash;
} else if (prev_state && !edit->active) {
/* current edit is now cold */
win->edit.active = nk_false;
} return ret_flags;
}
NK_API nk_flags
nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags,
char *buffer, int max, nk_plugin_filter filter)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add feature: Multiline edit box with automatic text wrap.

{
Expand Down
1 change: 1 addition & 0 deletions src/nuklear_popup.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ nk_nonblock_begin(struct nk_context *ctx,
root->flags |= NK_WINDOW_REMOVE_ROM;
root = root->parent;
}
win->popup.buf.active = is_active;
return is_active;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix: combo boxes crashes program (points to invalid memory) in draw rotine.

}
popup->bounds = body;
Expand Down
9 changes: 7 additions & 2 deletions src/nuklear_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len)
NK_API int
nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len)
{
return nk_str_insert_text_utf8(str, pos, text, len);
NK_ASSERT(str);
NK_ASSERT(text);
if (!str || !text || !len) return 0;

nk_str_insert_at_char(str, pos, text, len);
return len;
}
NK_API int
nk_str_insert_str_char(struct nk_str *str, int pos, const char *text)
{
return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text));
return nk_str_insert_text_char(str, pos, text, nk_strlen(text));
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix: weird behavior on multibyte characters operations.

NK_API int
nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len)
Expand Down
11 changes: 9 additions & 2 deletions src/nuklear_text_editor.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len)
{
/* API paste: replace existing selection with passed-in text */
int glyphs;
int glyph_len;
nk_rune unicode;
char *str_cursor;
int cursor;
const char *text = (const char *) ctext;
if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0;

Expand All @@ -342,10 +346,13 @@ nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len)
nk_textedit_delete_selection(state);

/* try to insert the characters */
str_cursor = nk_str_at_rune(&state->string, state->cursor, &unicode, &glyph_len);
cursor = (void *)str_cursor - state->string.buffer.memory.ptr;

glyphs = nk_utf_len(ctext, len);
if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) {
if (nk_str_insert_text_char(&state->string, cursor, text, len)) {
nk_textedit_makeundo_insert(state, state->cursor, glyphs);
state->cursor += len;
state->cursor += glyphs;
state->has_preferred_x = 0;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix: weird behavior on multibyte characters operations.

return 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/nuklear_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ nk_text_calculate_text_bounds(const struct nk_user_font *font,

*glyphs = 0;
while ((text_len < byte_len) && glyph_len) {
if (unicode == '\n') {
if (unicode == '\n' || unicode == '\v') {
text_size.x = NK_MAX(text_size.x, line_width);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For edit auto wrap feature. Consider a vertical tab (\v) as a line break too.

text_size.y += line_height;
line_width = 0;
Expand Down