1- // stb_textedit.h - v1.11 - public domain - Sean Barrett
1+ // stb_textedit.h - v1.13 - public domain - Sean Barrett
22// Development of this library was sponsored by RAD Game Tools
33//
44// This C header file implements the guts of a multi-line text-editing
1313// texts, as its performance does not scale and it has limited undo).
1414//
1515// Non-trivial behaviors are modelled after Windows text controls.
16- //
16+ //
1717//
1818// LICENSE
1919//
2929//
3030// VERSION HISTORY
3131//
32+ // 1.13 (2019-02-07) fix bug in undo size management
33+ // 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
3234// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
3335// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
3436// 1.9 (2016-08-27) customizable move-by-word
8587// moderate sizes. The undo system does no memory allocations, so
8688// it grows STB_TexteditState by the worst-case storage which is (in bytes):
8789//
88- // [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
89- // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT
90+ // [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
91+ // + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT
9092//
9193//
9294// Implementation mode:
109111// Symbols that must be the same in header-file and implementation mode:
110112//
111113// STB_TEXTEDIT_CHARTYPE the character type
112- // STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position
114+ // STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position
113115// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow
114116// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer
115117//
198200// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
199201// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
200202// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
201- // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key)
203+ // void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
202204//
203205// Each of these functions potentially updates the string and updates the
204206// state.
211213// call this with the mouse x,y on a mouse down; it will update the cursor
212214// and reset the selection start/end to the cursor point. the x,y must
213215// be relative to the text widget, with (0,0) being the top left.
214- //
216+ //
215217// drag:
216218// call this with the mouse x,y on a mouse drag/up; it will update the
217219// cursor and the selection end point
218- //
220+ //
219221// cut:
220222// call this to delete the current selection; returns true if there was
221223// one. you should FIRST copy the current selection to the system paste buffer.
222224// (To copy, just copy the current selection out of the string yourself.)
223- //
225+ //
224226// paste:
225227// call this to paste text at the current cursor point or over the current
226228// selection if there is one.
227- //
229+ //
228230// key:
229231// call this for keyboard inputs sent to the textfield. you can use it
230232// for "key down" events or for "translated" key events. if you need to
231233// do both (as in Win32), or distinguish Unicode characters from control
232234// inputs, set a high bit to distinguish the two; then you can define the
233235// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
234236// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
235- // clear.
237+ // clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
238+ // anything other type you wante before including.
236239//
240+ //
237241// When rendering, you can read the cursor position and selection state from
238242// the STB_TexteditState.
239243//
@@ -292,9 +296,9 @@ typedef struct
292296{
293297 // private data
294298 STB_TEXTEDIT_POSITIONTYPE where ;
295- short insert_length ;
296- short delete_length ;
297- short char_storage ;
299+ STB_TEXTEDIT_POSITIONTYPE insert_length ;
300+ STB_TEXTEDIT_POSITIONTYPE delete_length ;
301+ int char_storage ;
298302} StbUndoRecord ;
299303
300304typedef struct
@@ -303,7 +307,7 @@ typedef struct
303307 StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT ];
304308 STB_TEXTEDIT_CHARTYPE undo_char [STB_TEXTEDIT_UNDOCHARCOUNT ];
305309 short undo_point , redo_point ;
306- short undo_char_point , redo_char_point ;
310+ int undo_char_point , redo_char_point ;
307311} StbUndoState ;
308312
309313typedef struct
@@ -555,7 +559,6 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s
555559
556560 // now scan to find xpos
557561 find -> x = r .x0 ;
558- i = 0 ;
559562 for (i = 0 ; first + i < n ; ++ i )
560563 find -> x += STB_TEXTEDIT_GETWIDTH (str , first , i );
561564}
@@ -634,11 +637,9 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat
634637}
635638
636639#ifdef STB_TEXTEDIT_IS_SPACE
637- static int is_word_boundary ( STB_TEXTEDIT_STRING * _str , int _idx )
640+ static int is_word_boundary ( STB_TEXTEDIT_STRING * str , int idx )
638641{
639- return _idx > 0 ? ((STB_TEXTEDIT_IS_SPACE (STB_TEXTEDIT_GETCHAR (_str ,_idx - 1 )) ||
640- STB_TEXTEDIT_IS_PUNCT (STB_TEXTEDIT_GETCHAR (_str ,_idx - 1 ))) &&
641- !STB_TEXTEDIT_IS_SPACE (STB_TEXTEDIT_GETCHAR (_str , _idx ))) : 1 ;
642+ return idx > 0 ? (STB_TEXTEDIT_IS_SPACE ( STB_TEXTEDIT_GETCHAR (str ,idx - 1 ) ) && !STB_TEXTEDIT_IS_SPACE ( STB_TEXTEDIT_GETCHAR (str , idx ) ) ) : 1 ;
642643}
643644
644645#ifndef STB_TEXTEDIT_MOVEWORDLEFT
@@ -687,7 +688,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
687688static int stb_textedit_cut (STB_TEXTEDIT_STRING * str , STB_TexteditState * state )
688689{
689690 if (STB_TEXT_HAS_SELECTION (state )) {
690- stb_textedit_delete_selection (str ,state ); // implicity clamps
691+ stb_textedit_delete_selection (str ,state ); // implicitly clamps
691692 state -> has_preferred_x = 0 ;
692693 return 1 ;
693694 }
@@ -713,8 +714,12 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta
713714 return 0 ;
714715}
715716
717+ #ifndef STB_TEXTEDIT_KEYTYPE
718+ #define STB_TEXTEDIT_KEYTYPE int
719+ #endif
720+
716721// API key: process a keyboard input
717- static void stb_textedit_key (STB_TEXTEDIT_STRING * str , STB_TexteditState * state , int key )
722+ static void stb_textedit_key (STB_TEXTEDIT_STRING * str , STB_TexteditState * state , STB_TEXTEDIT_KEYTYPE key )
718723{
719724retry :
720725 switch (key ) {
@@ -735,7 +740,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
735740 state -> has_preferred_x = 0 ;
736741 }
737742 } else {
738- stb_textedit_delete_selection (str ,state ); // implicity clamps
743+ stb_textedit_delete_selection (str ,state ); // implicitly clamps
739744 if (STB_TEXTEDIT_INSERTCHARS (str , state -> cursor , & ch , 1 )) {
740745 stb_text_makeundo_insert (state , state -> cursor , 1 );
741746 ++ state -> cursor ;
@@ -751,7 +756,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
751756 state -> insert_mode = !state -> insert_mode ;
752757 break ;
753758#endif
754-
759+
755760 case STB_TEXTEDIT_K_UNDO :
756761 stb_text_undo (str , state );
757762 state -> has_preferred_x = 0 ;
@@ -766,7 +771,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
766771 // if currently there's a selection, move cursor to start of selection
767772 if (STB_TEXT_HAS_SELECTION (state ))
768773 stb_textedit_move_to_first (state );
769- else
774+ else
770775 if (state -> cursor > 0 )
771776 -- state -> cursor ;
772777 state -> has_preferred_x = 0 ;
@@ -815,7 +820,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
815820
816821#ifdef STB_TEXTEDIT_MOVEWORDRIGHT
817822 case STB_TEXTEDIT_K_WORDRIGHT :
818- if (STB_TEXT_HAS_SELECTION (state ))
823+ if (STB_TEXT_HAS_SELECTION (state ))
819824 stb_textedit_move_to_last (str , state );
820825 else {
821826 state -> cursor = STB_TEXTEDIT_MOVEWORDRIGHT (str , state -> cursor );
@@ -893,7 +898,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
893898 }
894899 break ;
895900 }
896-
901+
897902 case STB_TEXTEDIT_K_UP :
898903 case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT : {
899904 StbFindState find ;
@@ -970,7 +975,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
970975 }
971976 state -> has_preferred_x = 0 ;
972977 break ;
973-
978+
974979#ifdef STB_TEXTEDIT_K_TEXTSTART2
975980 case STB_TEXTEDIT_K_TEXTSTART2 :
976981#endif
@@ -987,7 +992,7 @@ static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state,
987992 state -> select_start = state -> select_end = 0 ;
988993 state -> has_preferred_x = 0 ;
989994 break ;
990-
995+
991996#ifdef STB_TEXTEDIT_K_TEXTSTART2
992997 case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT :
993998#endif
@@ -1091,11 +1096,11 @@ static void stb_textedit_discard_undo(StbUndoState *state)
10911096 if (state -> undo_rec [0 ].char_storage >= 0 ) {
10921097 int n = state -> undo_rec [0 ].insert_length , i ;
10931098 // delete n characters from all other records
1094- state -> undo_char_point = state -> undo_char_point - ( short ) n ; // vsnet05
1099+ state -> undo_char_point -= n ;
10951100 STB_TEXTEDIT_memmove (state -> undo_char , state -> undo_char + n , (size_t ) (state -> undo_char_point * sizeof (STB_TEXTEDIT_CHARTYPE )));
10961101 for (i = 0 ; i < state -> undo_point ; ++ i )
10971102 if (state -> undo_rec [i ].char_storage >= 0 )
1098- state -> undo_rec [i ].char_storage = state -> undo_rec [ i ]. char_storage - ( short ) n ; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it
1103+ state -> undo_rec [i ].char_storage -= n ; // @OPTIMIZE: get rid of char_storage and infer it
10991104 }
11001105 -- state -> undo_point ;
11011106 STB_TEXTEDIT_memmove (state -> undo_rec , state -> undo_rec + 1 , (size_t ) (state -> undo_point * sizeof (state -> undo_rec [0 ])));
@@ -1109,18 +1114,22 @@ static void stb_textedit_discard_undo(StbUndoState *state)
11091114static void stb_textedit_discard_redo (StbUndoState * state )
11101115{
11111116 int k = STB_TEXTEDIT_UNDOSTATECOUNT - 1 ;
1117+
11121118 if (state -> redo_point <= k ) {
11131119 // if the k'th undo state has characters, clean those up
11141120 if (state -> undo_rec [k ].char_storage >= 0 ) {
11151121 int n = state -> undo_rec [k ].insert_length , i ;
1116- // delete n characters from all other records
1117- state -> redo_char_point = state -> redo_char_point + ( short ) n ; // vsnet05
1122+ // move the remaining redo character data to the end of the buffer
1123+ state -> redo_char_point += n ;
11181124 STB_TEXTEDIT_memmove (state -> undo_char + state -> redo_char_point , state -> undo_char + state -> redo_char_point - n , (size_t ) ((STB_TEXTEDIT_UNDOCHARCOUNT - state -> redo_char_point )* sizeof (STB_TEXTEDIT_CHARTYPE )));
1125+ // adjust the position of all the other records to account for above memmove
11191126 for (i = state -> redo_point ; i < k ; ++ i )
11201127 if (state -> undo_rec [i ].char_storage >= 0 )
1121- state -> undo_rec [i ].char_storage = state -> undo_rec [ i ]. char_storage + ( short ) n ; // vsnet05
1128+ state -> undo_rec [i ].char_storage += n ;
11221129 }
1123- STB_TEXTEDIT_memmove (state -> undo_rec + state -> redo_point , state -> undo_rec + state -> redo_point - 1 , (size_t ) ((size_t )(STB_TEXTEDIT_UNDOSTATECOUNT - state -> redo_point )* sizeof (state -> undo_rec [0 ])));
1130+ // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
1131+ STB_TEXTEDIT_memmove (state -> undo_rec + state -> redo_point + 1 , state -> undo_rec + state -> redo_point , (size_t ) ((STB_TEXTEDIT_UNDOSTATECOUNT - state -> redo_point )* sizeof (state -> undo_rec [0 ])));
1132+ // now move redo_point to point to the new one
11241133 ++ state -> redo_point ;
11251134 }
11261135}
@@ -1156,15 +1165,15 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos,
11561165 return NULL ;
11571166
11581167 r -> where = pos ;
1159- r -> insert_length = (short ) insert_len ;
1160- r -> delete_length = (short ) delete_len ;
1168+ r -> insert_length = (STB_TEXTEDIT_POSITIONTYPE ) insert_len ;
1169+ r -> delete_length = (STB_TEXTEDIT_POSITIONTYPE ) delete_len ;
11611170
11621171 if (insert_len == 0 ) {
11631172 r -> char_storage = -1 ;
11641173 return NULL ;
11651174 } else {
11661175 r -> char_storage = state -> undo_char_point ;
1167- state -> undo_char_point = state -> undo_char_point + ( short ) insert_len ;
1176+ state -> undo_char_point += insert_len ;
11681177 return & state -> undo_char [r -> char_storage ];
11691178 }
11701179}
@@ -1204,16 +1213,16 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
12041213
12051214 // there's definitely room to store the characters eventually
12061215 while (s -> undo_char_point + u .delete_length > s -> redo_char_point ) {
1207- // there's currently not enough room, so discard a redo record
1208- stb_textedit_discard_redo (s );
12091216 // should never happen:
12101217 if (s -> redo_point == STB_TEXTEDIT_UNDOSTATECOUNT )
12111218 return ;
1219+ // there's currently not enough room, so discard a redo record
1220+ stb_textedit_discard_redo (s );
12121221 }
12131222 r = & s -> undo_rec [s -> redo_point - 1 ];
12141223
12151224 r -> char_storage = s -> redo_char_point - u .delete_length ;
1216- s -> redo_char_point = s -> redo_char_point - ( short ) u .delete_length ;
1225+ s -> redo_char_point = s -> redo_char_point - u .delete_length ;
12171226
12181227 // now save the characters
12191228 for (i = 0 ; i < u .delete_length ; ++ i )
@@ -1358,38 +1367,38 @@ This software is available under 2 licenses -- choose whichever you prefer.
13581367------------------------------------------------------------------------------
13591368ALTERNATIVE A - MIT License
13601369Copyright (c) 2017 Sean Barrett
1361- Permission is hereby granted, free of charge, to any person obtaining a copy of
1362- this software and associated documentation files (the "Software"), to deal in
1363- the Software without restriction, including without limitation the rights to
1364- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1365- of the Software, and to permit persons to whom the Software is furnished to do
1370+ Permission is hereby granted, free of charge, to any person obtaining a copy of
1371+ this software and associated documentation files (the "Software"), to deal in
1372+ the Software without restriction, including without limitation the rights to
1373+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1374+ of the Software, and to permit persons to whom the Software is furnished to do
13661375so, subject to the following conditions:
1367- The above copyright notice and this permission notice shall be included in all
1376+ The above copyright notice and this permission notice shall be included in all
13681377copies or substantial portions of the Software.
1369- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1370- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1371- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1372- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1373- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1374- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1378+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1379+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1380+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1381+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1382+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1383+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
13751384SOFTWARE.
13761385------------------------------------------------------------------------------
13771386ALTERNATIVE B - Public Domain (www.unlicense.org)
13781387This is free and unencumbered software released into the public domain.
1379- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1380- software, either in source code form or as a compiled binary, for any purpose,
1388+ Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1389+ software, either in source code form or as a compiled binary, for any purpose,
13811390commercial or non-commercial, and by any means.
1382- In jurisdictions that recognize copyright laws, the author or authors of this
1383- software dedicate any and all copyright interest in the software to the public
1384- domain. We make this dedication for the benefit of the public at large and to
1385- the detriment of our heirs and successors. We intend this dedication to be an
1386- overt act of relinquishment in perpetuity of all present and future rights to
1391+ In jurisdictions that recognize copyright laws, the author or authors of this
1392+ software dedicate any and all copyright interest in the software to the public
1393+ domain. We make this dedication for the benefit of the public at large and to
1394+ the detriment of our heirs and successors. We intend this dedication to be an
1395+ overt act of relinquishment in perpetuity of all present and future rights to
13871396this software under copyright law.
1388- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1389- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1390- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1391- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1392- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1397+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1398+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1399+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1400+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1401+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
13931402WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13941403------------------------------------------------------------------------------
13951404*/
0 commit comments