From e63b3e0702b26902d9760b40cce431a9c3087cc1 Mon Sep 17 00:00:00 2001 From: Sean Barrett Date: Tue, 27 May 2014 22:56:57 -0700 Subject: [PATCH] fix some stuff that used RAD types and so were totally broken, add a compile test/sample --- stb_textedit.h | 80 +++++++++++++++++++++++------------------------ tests/stb.dsp | 6 +++- tests/textedit_sample.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 41 deletions(-) create mode 100644 tests/textedit_sample.c diff --git a/stb_textedit.h b/stb_textedit.h index 1dd227d..bfe7967 100644 --- a/stb_textedit.h +++ b/stb_textedit.h @@ -1,4 +1,4 @@ -// stb_textedit.h - v1.1 - public domain - Sean Barrett +// stb_textedit.h - v1.2 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools // // This C header file implements the guts of a multi-line text-editing @@ -30,6 +30,7 @@ // // VERSION HISTORY // +// 1.2 (2013-05-27) fix some RAD types that had crept into the new code // 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) // 1.0 (2012-07-26) improve documentation, initial public release // 0.3 (2012-02-24) bugfixes, single-line mode; insert mode @@ -419,7 +420,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat state->cursor = stb_text_locate_coord(str, x, y); state->select_start = state->cursor; state->select_end = state->cursor; - state->has_preferred_x = false; + state->has_preferred_x = 0; } // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location @@ -529,7 +530,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta { stb_text_makeundo_delete(str, state, where, len); STB_TEXTEDIT_DELETECHARS(str, where, len); - state->has_preferred_x = false; + state->has_preferred_x = 0; } // delete the section @@ -544,7 +545,7 @@ static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_Textedit stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); state->select_start = state->cursor = state->select_end; } - state->has_preferred_x = false; + state->has_preferred_x = 0; } } @@ -565,7 +566,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state) stb_textedit_sortselection(state); state->cursor = state->select_start; state->select_end = state->select_start; - state->has_preferred_x = false; + state->has_preferred_x = 0; } } @@ -577,16 +578,14 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat stb_textedit_clamp(str, state); state->cursor = state->select_end; state->select_start = state->select_end; - state->has_preferred_x = false; + state->has_preferred_x = 0; } } #ifdef STB_TEXTEDIT_IS_SPACE -static rrbool is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) +static int is_word_boundary( STB_TEXTEDIT_STRING *_str, int _idx ) { - RR_ASSERT( _idx < STB_TEXTEDIT_STRINGLEN(_str) ); - - return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : true; + return _idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str,_idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(_str, _idx) ) ) : 1; } static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *_str, STB_TexteditState *_state ) @@ -629,15 +628,16 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_delete_selection(str,state); // implicity clamps - state->has_preferred_x = false; + state->has_preferred_x = 0; return 1; } return 0; } // API paste: replace existing selection with passed-in text -static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) { + STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; // if there's a selection, the paste should delete it stb_textedit_clamp(str, state); stb_textedit_delete_selection(str,state); @@ -645,7 +645,7 @@ static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { stb_text_makeundo_insert(str, state, state->cursor, len); state->cursor += len; - state->has_preferred_x = false; + state->has_preferred_x = 0; return 1; } // remove the undo since we didn't actually insert the characters @@ -673,14 +673,14 @@ retry: STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { ++state->cursor; - state->has_preferred_x = false; + state->has_preferred_x = 0; } } else { stb_textedit_delete_selection(str,state); // implicity clamps if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { stb_text_makeundo_insert(str, state, state->cursor, 1); ++state->cursor; - state->has_preferred_x = false; + state->has_preferred_x = 0; } } } @@ -695,12 +695,12 @@ retry: case STB_TEXTEDIT_K_UNDO: stb_text_undo(str, state); - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_REDO: stb_text_redo(str, state); - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_LEFT: @@ -710,7 +710,7 @@ retry: else if (state->cursor > 0) --state->cursor; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_RIGHT: @@ -720,7 +720,7 @@ retry: else ++state->cursor; stb_textedit_clamp(str, state); - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: @@ -730,7 +730,7 @@ retry: if (state->select_end > 0) --state->select_end; state->cursor = state->select_end; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; #ifdef STB_TEXTEDIT_IS_SPACE @@ -779,7 +779,7 @@ retry: ++state->select_end; stb_textedit_clamp(str, state); state->cursor = state->select_end; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_DOWN: @@ -805,14 +805,14 @@ retry: // now find character position down a row if (find.length) { - F32 goal_x = state->has_preferred_x ? state->preferred_x : find.x; - F32 x; - S32 start = find.first_char + find.length; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + int start = find.first_char + find.length; state->cursor = start; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; for (i=0; i < row.num_chars; ++i) { - F32 dx = STB_TEXTEDIT_GETWIDTH(str, start, i); + float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) break; @@ -824,7 +824,7 @@ retry: } stb_textedit_clamp(str, state); - state->has_preferred_x = true; + state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) @@ -857,13 +857,13 @@ retry: // can only go up if there's a previous row if (find.prev_first != find.first_char) { // now find character position up a row - F32 goal_x = state->has_preferred_x ? state->preferred_x : find.x; - F32 x; + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; state->cursor = find.prev_first; STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); x = row.x0; for (i=0; i < row.num_chars; ++i) { - F32 dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); + float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) break; @@ -875,7 +875,7 @@ retry: } stb_textedit_clamp(str, state); - state->has_preferred_x = true; + state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) @@ -893,7 +893,7 @@ retry: if (state->cursor < n) stb_textedit_delete(str, state, state->cursor, 1); } - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_BACKSPACE: @@ -907,30 +907,30 @@ retry: --state->cursor; } } - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_TEXTSTART: state->cursor = state->select_start = state->select_end = 0; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_TEXTEND: state->cursor = STB_TEXTEDIT_STRINGLEN(str); state->select_start = state->select_end = 0; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = 0; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: stb_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); - state->has_preferred_x = false; + state->has_preferred_x = 0; break; @@ -940,7 +940,7 @@ retry: stb_textedit_move_to_first(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); state->cursor = find.first_char; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; } @@ -950,7 +950,7 @@ retry: stb_textedit_move_to_first(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); state->cursor = find.first_char + find.length; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; } @@ -960,7 +960,7 @@ retry: stb_textedit_prep_selection_at_cursor(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); state->select_end = find.first_char; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; } @@ -970,7 +970,7 @@ retry: stb_textedit_prep_selection_at_cursor(state); stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); state->select_end = find.first_char + find.length; - state->has_preferred_x = false; + state->has_preferred_x = 0; break; } diff --git a/tests/stb.dsp b/tests/stb.dsp index 9a5a23a..5b9e1a6 100644 --- a/tests/stb.dsp +++ b/tests/stb.dsp @@ -66,7 +66,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /GX /Zd /Od /I "..\.." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MAIN_TEST" /FR /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Zd /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "MAIN_TEST" /FR /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" @@ -104,5 +104,9 @@ SOURCE=..\stb_vorbis.c SOURCE=.\test_truetype.c # End Source File +# Begin Source File + +SOURCE=.\textedit_sample.c +# End Source File # End Target # End Project diff --git a/tests/textedit_sample.c b/tests/textedit_sample.c new file mode 100644 index 0000000..32cf287 --- /dev/null +++ b/tests/textedit_sample.c @@ -0,0 +1,82 @@ +// I haven't actually tested this yet, this is just to make sure it compiles + +#include +#include // memmove +#include // isspace + +#define STB_TEXTEDIT_CHARTYPE char +#define STB_TEXTEDIT_STRING text_control + +// get the base type +#include "stb_textedit.h" + +// define our editor structure +typedef struct +{ + char *string; + int stringlen; + STB_TexteditState state; +} text_control; + +// define the functions we need +void layout_func(StbTexteditRow *row, STB_TEXTEDIT_STRING *str, int start_i) +{ + int remaining_chars = str->stringlen - start_i; + row->num_chars = remaining_chars > 20 ? 20 : remaining_chars; // should do real word wrap here + row->x0 = 0; + row->x1 = 20; // need to account for actual size of characters + row->baseline_y_delta = 1.25; + row->ymin = -1; + row->ymax = 0; +} + +int delete_chars(STB_TEXTEDIT_STRING *str, int pos, int num) +{ + memmove(&str->string[pos], &str->string[pos+num], str->stringlen - (pos-num)); + str->stringlen -= num; + return 1; // always succeeds +} + +int insert_chars(STB_TEXTEDIT_STRING *str, int pos, STB_TEXTEDIT_CHARTYPE *newtext, int num) +{ + str->string = realloc(str, str->stringlen + num); + memmove(&str->string[pos+num], &str->string[pos], str->stringlen - pos); + memcpy(&str->string[pos], newtext, num); + return 1; // always succeeds +} + +// define all the #defines needed + +#define KEYDOWN_BIT 0x80000000 +#define STB_TEXTEDIT_STRINGLEN(tc) ((tc)->stringlen) +#define STB_TEXTEDIT_LAYOUTROW layout_func +#define STB_TEXTEDIT_GETWIDTH(tc,n,i) (1) // quick hack for monospaced +#define STB_TEXTEDIT_KEYTOTEXT(key) (((key) & KEYDOWN_BIT) ? 0 : (key)) +#define STB_TEXTEDIT_GETCHAR(tc,i) ((tc)->string[i]) +#define STB_TEXTEDIT_NEWLINE '\n' +#define STB_TEXTEDIT_IS_SPACE(ch) isspace(ch) +#define STB_TEXTEDIT_DELETECHARS delete_chars +#define STB_TEXTEDIT_INSERTCHARS insert_chars + +#define STB_TEXTEDIT_K_SHIFT 0x40000000 +#define STB_TEXTEDIT_K_CONTROL 0x20000000 +#define STB_TEXTEDIT_K_LEFT (KEYDOWN_BIT | 1) // actually use VK_LEFT, SDLK_LEFT, etc +#define STB_TEXTEDIT_K_RIGHT (KEYDOWN_BIT | 2) // VK_RIGHT +#define STB_TEXTEDIT_K_UP (KEYDOWN_BIT | 3) // VK_UP +#define STB_TEXTEDIT_K_DOWN (KEYDOWN_BIT | 4) // VK_DOWN +#define STB_TEXTEDIT_K_LINESTART (KEYDOWN_BIT | 5) // VK_HOME +#define STB_TEXTEDIT_K_LINEEND (KEYDOWN_BIT | 6) // VK_END +#define STB_TEXTEDIT_K_TEXTSTART (STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_TEXTEND (STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_DELETE (KEYDOWN_BIT | 7) // VK_DELETE +#define STB_TEXTEDIT_K_BACKSPACE (KEYDOWN_BIT | 8) // VK_BACKSPACE +#define STB_TEXTEDIT_K_UNDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'z') +#define STB_TEXTEDIT_K_REDO (KEYDOWN_BIT | STB_TEXTEDIT_K_CONTROL | 'y') +#define STB_TEXTEDIT_K_INSERT (KEYDOWN_BIT | 9) // VK_INSERT +#define STB_TEXTEDIT_K_WORDLEFT (STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_WORDRIGHT (STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_CONTROL) +#define STB_TEXTEDIT_K_PGUP (KEYDOWN_BIT | 10) // VK_PGUP -- not implemented +#define STB_TEXTEDIT_K_PGDOWN (KEYDOWN_BIT | 11) // VK_PGDOWN -- not implemented + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h"