Changeset e66fd66 in opengl-game for IMGUI/imstb_textedit.h


Ignore:
Timestamp:
Dec 5, 2020, 7:14:31 PM (4 years ago)
Author:
Dmitry Portnoy <dportnoy@…>
Branches:
feature/imgui-sdl, master
Children:
95c657f
Parents:
78c3045
Message:

In OpenGLReference, change all enums to enum classes and update IMGUI to the latest version

File:
1 moved

Legend:

Unmodified
Added
Removed
  • IMGUI/imstb_textedit.h

    r78c3045 re66fd66  
    1 // [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb
    2 // [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815)
    3 // [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715)
    4 // [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681)
    5 // [ImGui] - fixed some minor warnings
    6 
    7 // stb_textedit.h - v1.9  - public domain - Sean Barrett
     1// [DEAR IMGUI]
     2// This is a slightly modified version of stb_textedit.h 1.13.
     3// Those changes would need to be pushed into nothings/stb:
     4// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
     5// Grep for [DEAR IMGUI] to find the changes.
     6
     7// stb_textedit.h - v1.13  - public domain - Sean Barrett
    88// Development of this library was sponsored by RAD Game Tools
    99//
     
    2424// LICENSE
    2525//
    26 //   This software is dual-licensed to the public domain and under the following
    27 //   license: you are granted a perpetual, irrevocable license to copy, modify,
    28 //   publish, and distribute this file as you see fit.
     26// See end of file for license information.
    2927//
    3028//
     
    3836// VERSION HISTORY
    3937//
     38//   1.13 (2019-02-07) fix bug in undo size management
     39//   1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
     40//   1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
     41//   1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
    4042//   1.9  (2016-08-27) customizable move-by-word
    4143//   1.8  (2016-04-02) better keyboard handling when mouse button is down
     
    5658//   Ulf Winklemann: move-by-word in 1.1
    5759//   Fabian Giesen: secondary key inputs in 1.5
    58 //   Martins Mozeiko: STB_TEXTEDIT_memmove
     60//   Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
    5961//
    6062//   Bugfixes:
     
    6264//      Daniel Keller
    6365//      Omar Cornut
     66//      Dan Thompson
    6467//
    6568// USAGE
     
    9194//   it grows STB_TexteditState by the worst-case storage which is (in bytes):
    9295//
    93 //        [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
    94 //      +      sizeof(STB_TEXTEDIT_CHARTYPE)      * STB_TEXTEDIT_UNDOCHAR_COUNT
     96//        [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT
     97//      +          sizeof(STB_TEXTEDIT_CHARTYPE)      * STB_TEXTEDIT_UNDOCHAR_COUNT
    9598//
    9699//
     
    115118//
    116119//     STB_TEXTEDIT_CHARTYPE             the character type
    117 //     STB_TEXTEDIT_POSITIONTYPE         small type that a valid cursor position
     120//     STB_TEXTEDIT_POSITIONTYPE         small type that is a valid cursor position
    118121//     STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow
    119122//     STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer
     
    146149//    STB_TEXTEDIT_K_UP          keyboard input to move cursor up
    147150//    STB_TEXTEDIT_K_DOWN        keyboard input to move cursor down
     151//    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page
     152//    STB_TEXTEDIT_K_PGDOWN      keyboard input to move cursor down a page
    148153//    STB_TEXTEDIT_K_LINESTART   keyboard input to move cursor to start of line  // e.g. HOME
    149154//    STB_TEXTEDIT_K_LINEEND     keyboard input to move cursor to end of line    // e.g. END
     
    168173//    STB_TEXTEDIT_K_TEXTEND2            secondary keyboard input to move cursor to end of text
    169174//
    170 // Todo:
    171 //    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page
    172 //    STB_TEXTEDIT_K_PGDOWN      keyboard input to move cursor down a page
    173 //
    174175// Keyboard input must be encoded as a single integer value; e.g. a character code
    175176// and some bitflags that represent shift states. to simplify the interface, SHIFT must
    176177// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
    177 // i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
     178// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
    178179//
    179180// You can encode other things, such as CONTROL or ALT, in additional bits, and
     
    204205//    int  stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
    205206//    int  stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
    206 //    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key)
     207//    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
    207208//
    208209//    Each of these functions potentially updates the string and updates the
     
    238239//          various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
    239240//          set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
    240 //          clear.
     241//          clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
     242//          anything other type you wante before including.
     243//
    241244//     
    242245//   When rendering, you can read the cursor position and selection state from
     
    298301   // private data
    299302   STB_TEXTEDIT_POSITIONTYPE  where;
    300    short           insert_length;
    301    short           delete_length;
    302    short           char_storage;
     303   STB_TEXTEDIT_POSITIONTYPE  insert_length;
     304   STB_TEXTEDIT_POSITIONTYPE  delete_length;
     305   int                        char_storage;
    303306} StbUndoRecord;
    304307
     
    306309{
    307310   // private data
    308    StbUndoRecord          undo_rec[STB_TEXTEDIT_UNDOSTATECOUNT];
     311   StbUndoRecord          undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];
    309312   STB_TEXTEDIT_CHARTYPE  undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];
    310313   short undo_point, redo_point;
    311    short undo_char_point, redo_char_point;
     314   int undo_char_point, redo_char_point;
    312315} StbUndoState;
    313316
     
    332335   // each textfield keeps its own insert mode state. to keep an app-wide
    333336   // insert mode, copy this value in/out of the app state
     337
     338   int row_count_per_page;
     339   // page size in number of row.
     340   // this value MUST be set to >0 for pageup or pagedown in multilines documents.
    334341
    335342   /////////////////////
     
    357364typedef struct
    358365{
    359    float x0, x1;             // starting x location, end x location (allows for align=right, etc)
     366   float x0,x1;             // starting x location, end x location (allows for align=right, etc)
    360367   float baseline_y_delta;  // position of baseline relative to previous row's baseline
    361    float ymin, ymax;         // height of row above and below baseline
     368   float ymin,ymax;         // height of row above and below baseline
    362369   int num_chars;
    363370} StbTexteditRow;
     
    394401   int n = STB_TEXTEDIT_STRINGLEN(str);
    395402   float base_y = 0, prev_x;
    396    int i = 0, k;
     403   int i=0, k;
    397404
    398405   r.x0 = r.x1 = 0;
     
    406413         return n;
    407414
    408       if (i == 0 && y < base_y + r.ymin)
     415      if (i==0 && y < base_y + r.ymin)
    409416         return 0;
    410417
     
    428435      // search characters in row for one that straddles 'x'
    429436      prev_x = r.x0;
    430       for (k = 0; k < r.num_chars; ++k) {
     437      for (k=0; k < r.num_chars; ++k) {
    431438         float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
    432          if (x < prev_x + w) {
    433             if (x < prev_x + w / 2)
    434                return k + i;
     439         if (x < prev_x+w) {
     440            if (x < prev_x+w/2)
     441               return k+i;
    435442            else
    436                return k + i + 1;
     443               return k+i+1;
    437444         }
    438445         prev_x += w;
     
    442449
    443450   // if the last character is a newline, return that. otherwise return 'after' the last character
    444    if (STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) == STB_TEXTEDIT_NEWLINE)
    445       return i + r.num_chars - 1;
     451   if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
     452      return i+r.num_chars-1;
    446453   else
    447       return i + r.num_chars;
     454      return i+r.num_chars;
    448455}
    449456
     
    451458static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
    452459{
     460   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
     461   // goes off the top or bottom of the text
     462   if( state->single_line )
     463   {
     464      StbTexteditRow r;
     465      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
     466      y = r.ymin;
     467   }
     468
    453469   state->cursor = stb_text_locate_coord(str, x, y);
    454470   state->select_start = state->cursor;
     
    460476static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
    461477{
    462    int p = stb_text_locate_coord(str, x, y);
     478   int p = 0;
     479
     480   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
     481   // goes off the top or bottom of the text
     482   if( state->single_line )
     483   {
     484      StbTexteditRow r;
     485      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
     486      y = r.ymin;
     487   }
     488
    463489   if (state->select_start == state->select_end)
    464490      state->select_start = state->cursor;
     491
     492   p = stb_text_locate_coord(str, x, y);
    465493   state->cursor = state->select_end = p;
    466494}
     
    480508typedef struct
    481509{
    482    float x, y;    // position of n'th character
     510   float x,y;    // position of n'th character
    483511   float height; // height of line
    484512   int first_char, length; // first char of row, and length
     
    493521   int prev_start = 0;
    494522   int z = STB_TEXTEDIT_STRINGLEN(str);
    495    int i = 0, first;
     523   int i=0, first;
    496524
    497525   if (n == z) {
     
    505533         find->height = r.ymax - r.ymin;
    506534         find->x = r.x1;
    507       }
    508       else {
     535      } else {
    509536         find->y = 0;
    510537         find->x = 0;
     
    525552   find->y = 0;
    526553
    527    for (;;) {
     554   for(;;) {
    528555      STB_TEXTEDIT_LAYOUTROW(&r, str, i);
    529556      if (n < i + r.num_chars)
     
    541568   // now scan to find xpos
    542569   find->x = r.x0;
    543    i = 0;
    544    for (i = 0; first + i < n; ++i)
     570   for (i=0; first+i < n; ++i)
    545571      find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
    546572}
     
    578604         stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start);
    579605         state->select_end = state->cursor = state->select_start;
    580       }
    581       else {
     606      } else {
    582607         stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end);
    583608         state->select_start = state->cursor = state->select_end;
     
    621646
    622647#ifdef STB_TEXTEDIT_IS_SPACE
    623 static int is_word_boundary(STB_TEXTEDIT_STRING *str, int idx)
    624 {
    625    return idx > 0 ? (STB_TEXTEDIT_IS_SPACE(STB_TEXTEDIT_GETCHAR(str, idx - 1)) && !STB_TEXTEDIT_IS_SPACE(STB_TEXTEDIT_GETCHAR(str, idx))) : 1;
     648static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )
     649{
     650   return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
    626651}
    627652
    628653#ifndef STB_TEXTEDIT_MOVEWORDLEFT
    629 static int stb_textedit_move_to_word_previous(STB_TEXTEDIT_STRING *str, int c)
     654static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )
    630655{
    631656   --c; // always move at least one character
    632    while (c >= 0 && !is_word_boundary(str, c))
     657   while( c >= 0 && !is_word_boundary( str, c ) )
    633658      --c;
    634659
    635    if (c < 0)
     660   if( c < 0 )
    636661      c = 0;
    637662
     
    642667
    643668#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
    644 static int stb_textedit_move_to_word_next(STB_TEXTEDIT_STRING *str, int c)
     669static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )
    645670{
    646671   const int len = STB_TEXTEDIT_STRINGLEN(str);
    647672   ++c; // always move at least one character
    648    while (c < len && !is_word_boundary(str, c))
     673   while( c < len && !is_word_boundary( str, c ) )
    649674      ++c;
    650675
    651    if (c > len)
     676   if( c > len )
    652677      c = len;
    653678
     
    672697{
    673698   if (STB_TEXT_HAS_SELECTION(state)) {
    674       stb_textedit_delete_selection(str, state); // implicity clamps
     699      stb_textedit_delete_selection(str,state); // implicitly clamps
    675700      state->has_preferred_x = 0;
    676701      return 1;
     
    680705
    681706// API paste: replace existing selection with passed-in text
    682 static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *text, int len)
     707static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
    683708{
    684709   // if there's a selection, the paste should delete it
    685710   stb_textedit_clamp(str, state);
    686    stb_textedit_delete_selection(str, state);
     711   stb_textedit_delete_selection(str,state);
    687712   // try to insert the characters
    688713   if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
     
    698723}
    699724
     725#ifndef STB_TEXTEDIT_KEYTYPE
     726#define STB_TEXTEDIT_KEYTYPE int
     727#endif
     728
    700729// API key: process a keyboard input
    701 static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key)
     730static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
    702731{
    703732retry:
    704733   switch (key) {
    705    default: {
    706       int c = STB_TEXTEDIT_KEYTOTEXT(key);
    707       if (c > 0) {
    708          STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE)c;
    709 
    710          // can't add newline in single-line mode
    711          if (c == '\n' && state->single_line)
    712             break;
    713 
    714          if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
    715             stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
    716             STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
    717             if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
    718                ++state->cursor;
    719                state->has_preferred_x = 0;
     734      default: {
     735         int c = STB_TEXTEDIT_KEYTOTEXT(key);
     736         if (c > 0) {
     737            STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;
     738
     739            // can't add newline in single-line mode
     740            if (c == '\n' && state->single_line)
     741               break;
     742
     743            if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
     744               stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
     745               STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
     746               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
     747                  ++state->cursor;
     748                  state->has_preferred_x = 0;
     749               }
     750            } else {
     751               stb_textedit_delete_selection(str,state); // implicitly clamps
     752               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
     753                  stb_text_makeundo_insert(state, state->cursor, 1);
     754                  ++state->cursor;
     755                  state->has_preferred_x = 0;
     756               }
    720757            }
    721758         }
     759         break;
     760      }
     761
     762#ifdef STB_TEXTEDIT_K_INSERT
     763      case STB_TEXTEDIT_K_INSERT:
     764         state->insert_mode = !state->insert_mode;
     765         break;
     766#endif
     767         
     768      case STB_TEXTEDIT_K_UNDO:
     769         stb_text_undo(str, state);
     770         state->has_preferred_x = 0;
     771         break;
     772
     773      case STB_TEXTEDIT_K_REDO:
     774         stb_text_redo(str, state);
     775         state->has_preferred_x = 0;
     776         break;
     777
     778      case STB_TEXTEDIT_K_LEFT:
     779         // if currently there's a selection, move cursor to start of selection
     780         if (STB_TEXT_HAS_SELECTION(state))
     781            stb_textedit_move_to_first(state);
     782         else
     783            if (state->cursor > 0)
     784               --state->cursor;
     785         state->has_preferred_x = 0;
     786         break;
     787
     788      case STB_TEXTEDIT_K_RIGHT:
     789         // if currently there's a selection, move cursor to end of selection
     790         if (STB_TEXT_HAS_SELECTION(state))
     791            stb_textedit_move_to_last(str, state);
     792         else
     793            ++state->cursor;
     794         stb_textedit_clamp(str, state);
     795         state->has_preferred_x = 0;
     796         break;
     797
     798      case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:
     799         stb_textedit_clamp(str, state);
     800         stb_textedit_prep_selection_at_cursor(state);
     801         // move selection left
     802         if (state->select_end > 0)
     803            --state->select_end;
     804         state->cursor = state->select_end;
     805         state->has_preferred_x = 0;
     806         break;
     807
     808#ifdef STB_TEXTEDIT_MOVEWORDLEFT
     809      case STB_TEXTEDIT_K_WORDLEFT:
     810         if (STB_TEXT_HAS_SELECTION(state))
     811            stb_textedit_move_to_first(state);
    722812         else {
    723             stb_textedit_delete_selection(str, state); // implicity clamps
    724             if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
    725                stb_text_makeundo_insert(state, state->cursor, 1);
     813            state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
     814            stb_textedit_clamp( str, state );
     815         }
     816         break;
     817
     818      case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:
     819         if( !STB_TEXT_HAS_SELECTION( state ) )
     820            stb_textedit_prep_selection_at_cursor(state);
     821
     822         state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
     823         state->select_end = state->cursor;
     824
     825         stb_textedit_clamp( str, state );
     826         break;
     827#endif
     828
     829#ifdef STB_TEXTEDIT_MOVEWORDRIGHT
     830      case STB_TEXTEDIT_K_WORDRIGHT:
     831         if (STB_TEXT_HAS_SELECTION(state))
     832            stb_textedit_move_to_last(str, state);
     833         else {
     834            state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
     835            stb_textedit_clamp( str, state );
     836         }
     837         break;
     838
     839      case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:
     840         if( !STB_TEXT_HAS_SELECTION( state ) )
     841            stb_textedit_prep_selection_at_cursor(state);
     842
     843         state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
     844         state->select_end = state->cursor;
     845
     846         stb_textedit_clamp( str, state );
     847         break;
     848#endif
     849
     850      case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
     851         stb_textedit_prep_selection_at_cursor(state);
     852         // move selection right
     853         ++state->select_end;
     854         stb_textedit_clamp(str, state);
     855         state->cursor = state->select_end;
     856         state->has_preferred_x = 0;
     857         break;
     858
     859      case STB_TEXTEDIT_K_DOWN:
     860      case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:
     861      case STB_TEXTEDIT_K_PGDOWN:
     862      case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {
     863         StbFindState find;
     864         StbTexteditRow row;
     865         int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
     866         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;
     867         int row_count = is_page ? state->row_count_per_page : 1;
     868
     869         if (!is_page && state->single_line) {
     870            // on windows, up&down in single-line behave like left&right
     871            key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
     872            goto retry;
     873         }
     874
     875         if (sel)
     876            stb_textedit_prep_selection_at_cursor(state);
     877         else if (STB_TEXT_HAS_SELECTION(state))
     878            stb_textedit_move_to_last(str, state);
     879
     880         // compute current position of cursor point
     881         stb_textedit_clamp(str, state);
     882         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
     883
     884         for (j = 0; j < row_count; ++j) {
     885            float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
     886            int start = find.first_char + find.length;
     887
     888            if (find.length == 0)
     889               break;
     890
     891            // [DEAR IMGUI]
     892            // going down while being on the last line shouldn't bring us to that line end
     893            if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
     894               break;
     895
     896            // now find character position down a row
     897            state->cursor = start;
     898            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
     899            x = row.x0;
     900            for (i=0; i < row.num_chars; ++i) {
     901               float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
     902               #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
     903               if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
     904                  break;
     905               #endif
     906               x += dx;
     907               if (x > goal_x)
     908                  break;
    726909               ++state->cursor;
    727                state->has_preferred_x = 0;
     910            }
     911            stb_textedit_clamp(str, state);
     912
     913            state->has_preferred_x = 1;
     914            state->preferred_x = goal_x;
     915
     916            if (sel)
     917               state->select_end = state->cursor;
     918
     919            // go to next line
     920            find.first_char = find.first_char + find.length;
     921            find.length = row.num_chars;
     922         }
     923         break;
     924      }
     925         
     926      case STB_TEXTEDIT_K_UP:
     927      case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:
     928      case STB_TEXTEDIT_K_PGUP:
     929      case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {
     930         StbFindState find;
     931         StbTexteditRow row;
     932         int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
     933         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;
     934         int row_count = is_page ? state->row_count_per_page : 1;
     935
     936         if (!is_page && state->single_line) {
     937            // on windows, up&down become left&right
     938            key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
     939            goto retry;
     940         }
     941
     942         if (sel)
     943            stb_textedit_prep_selection_at_cursor(state);
     944         else if (STB_TEXT_HAS_SELECTION(state))
     945            stb_textedit_move_to_first(state);
     946
     947         // compute current position of cursor point
     948         stb_textedit_clamp(str, state);
     949         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
     950
     951         for (j = 0; j < row_count; ++j) {
     952            float  x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
     953
     954            // can only go up if there's a previous row
     955            if (find.prev_first == find.first_char)
     956               break;
     957
     958            // now find character position up a row
     959            state->cursor = find.prev_first;
     960            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
     961            x = row.x0;
     962            for (i=0; i < row.num_chars; ++i) {
     963               float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
     964               #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
     965               if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
     966                  break;
     967               #endif
     968               x += dx;
     969               if (x > goal_x)
     970                  break;
     971               ++state->cursor;
     972            }
     973            stb_textedit_clamp(str, state);
     974
     975            state->has_preferred_x = 1;
     976            state->preferred_x = goal_x;
     977
     978            if (sel)
     979               state->select_end = state->cursor;
     980
     981            // go to previous line
     982            // (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
     983            prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
     984            while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
     985               --prev_scan;
     986            find.first_char = find.prev_first;
     987            find.prev_first = prev_scan;
     988         }
     989         break;
     990      }
     991
     992      case STB_TEXTEDIT_K_DELETE:
     993      case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:
     994         if (STB_TEXT_HAS_SELECTION(state))
     995            stb_textedit_delete_selection(str, state);
     996         else {
     997            int n = STB_TEXTEDIT_STRINGLEN(str);
     998            if (state->cursor < n)
     999               stb_textedit_delete(str, state, state->cursor, 1);
     1000         }
     1001         state->has_preferred_x = 0;
     1002         break;
     1003
     1004      case STB_TEXTEDIT_K_BACKSPACE:
     1005      case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:
     1006         if (STB_TEXT_HAS_SELECTION(state))
     1007            stb_textedit_delete_selection(str, state);
     1008         else {
     1009            stb_textedit_clamp(str, state);
     1010            if (state->cursor > 0) {
     1011               stb_textedit_delete(str, state, state->cursor-1, 1);
     1012               --state->cursor;
    7281013            }
    7291014         }
     1015         state->has_preferred_x = 0;
     1016         break;
     1017         
     1018#ifdef STB_TEXTEDIT_K_TEXTSTART2
     1019      case STB_TEXTEDIT_K_TEXTSTART2:
     1020#endif
     1021      case STB_TEXTEDIT_K_TEXTSTART:
     1022         state->cursor = state->select_start = state->select_end = 0;
     1023         state->has_preferred_x = 0;
     1024         break;
     1025
     1026#ifdef STB_TEXTEDIT_K_TEXTEND2
     1027      case STB_TEXTEDIT_K_TEXTEND2:
     1028#endif
     1029      case STB_TEXTEDIT_K_TEXTEND:
     1030         state->cursor = STB_TEXTEDIT_STRINGLEN(str);
     1031         state->select_start = state->select_end = 0;
     1032         state->has_preferred_x = 0;
     1033         break;
     1034       
     1035#ifdef STB_TEXTEDIT_K_TEXTSTART2
     1036      case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:
     1037#endif
     1038      case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:
     1039         stb_textedit_prep_selection_at_cursor(state);
     1040         state->cursor = state->select_end = 0;
     1041         state->has_preferred_x = 0;
     1042         break;
     1043
     1044#ifdef STB_TEXTEDIT_K_TEXTEND2
     1045      case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:
     1046#endif
     1047      case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:
     1048         stb_textedit_prep_selection_at_cursor(state);
     1049         state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str);
     1050         state->has_preferred_x = 0;
     1051         break;
     1052
     1053
     1054#ifdef STB_TEXTEDIT_K_LINESTART2
     1055      case STB_TEXTEDIT_K_LINESTART2:
     1056#endif
     1057      case STB_TEXTEDIT_K_LINESTART:
     1058         stb_textedit_clamp(str, state);
     1059         stb_textedit_move_to_first(state);
     1060         if (state->single_line)
     1061            state->cursor = 0;
     1062         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
     1063            --state->cursor;
     1064         state->has_preferred_x = 0;
     1065         break;
     1066
     1067#ifdef STB_TEXTEDIT_K_LINEEND2
     1068      case STB_TEXTEDIT_K_LINEEND2:
     1069#endif
     1070      case STB_TEXTEDIT_K_LINEEND: {
     1071         int n = STB_TEXTEDIT_STRINGLEN(str);
     1072         stb_textedit_clamp(str, state);
     1073         stb_textedit_move_to_first(state);
     1074         if (state->single_line)
     1075             state->cursor = n;
     1076         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
     1077             ++state->cursor;
     1078         state->has_preferred_x = 0;
     1079         break;
    7301080      }
    731       break;
    732    }
    733 
    734 #ifdef STB_TEXTEDIT_K_INSERT
    735    case STB_TEXTEDIT_K_INSERT:
    736       state->insert_mode = !state->insert_mode;
    737       break;
    738 #endif
    739 
    740    case STB_TEXTEDIT_K_UNDO:
    741       stb_text_undo(str, state);
    742       state->has_preferred_x = 0;
    743       break;
    744 
    745    case STB_TEXTEDIT_K_REDO:
    746       stb_text_redo(str, state);
    747       state->has_preferred_x = 0;
    748       break;
    749 
    750    case STB_TEXTEDIT_K_LEFT:
    751       // if currently there's a selection, move cursor to start of selection
    752       if (STB_TEXT_HAS_SELECTION(state))
    753          stb_textedit_move_to_first(state);
    754       else
    755          if (state->cursor > 0)
     1081
     1082#ifdef STB_TEXTEDIT_K_LINESTART2
     1083      case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:
     1084#endif
     1085      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
     1086         stb_textedit_clamp(str, state);
     1087         stb_textedit_prep_selection_at_cursor(state);
     1088         if (state->single_line)
     1089            state->cursor = 0;
     1090         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
    7561091            --state->cursor;
    757       state->has_preferred_x = 0;
    758       break;
    759 
    760    case STB_TEXTEDIT_K_RIGHT:
    761       // if currently there's a selection, move cursor to end of selection
    762       if (STB_TEXT_HAS_SELECTION(state))
    763          stb_textedit_move_to_last(str, state);
    764       else
    765          ++state->cursor;
    766       stb_textedit_clamp(str, state);
    767       state->has_preferred_x = 0;
    768       break;
    769 
    770    case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:
    771       stb_textedit_clamp(str, state);
    772       stb_textedit_prep_selection_at_cursor(state);
    773       // move selection left
    774       if (state->select_end > 0)
    775          --state->select_end;
    776       state->cursor = state->select_end;
    777       state->has_preferred_x = 0;
    778       break;
    779 
    780 #ifdef STB_TEXTEDIT_MOVEWORDLEFT
    781    case STB_TEXTEDIT_K_WORDLEFT:
    782       if (STB_TEXT_HAS_SELECTION(state))
    783          stb_textedit_move_to_first(state);
    784       else {
    785          state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
     1092         state->select_end = state->cursor;
     1093         state->has_preferred_x = 0;
     1094         break;
     1095
     1096#ifdef STB_TEXTEDIT_K_LINEEND2
     1097      case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
     1098#endif
     1099      case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
     1100         int n = STB_TEXTEDIT_STRINGLEN(str);
    7861101         stb_textedit_clamp(str, state);
     1102         stb_textedit_prep_selection_at_cursor(state);
     1103         if (state->single_line)
     1104             state->cursor = n;
     1105         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
     1106            ++state->cursor;
     1107         state->select_end = state->cursor;
     1108         state->has_preferred_x = 0;
     1109         break;
    7871110      }
    788       break;
    789 
    790    case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:
    791       if (!STB_TEXT_HAS_SELECTION(state))
    792          stb_textedit_prep_selection_at_cursor(state);
    793 
    794       state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
    795       state->select_end = state->cursor;
    796 
    797       stb_textedit_clamp(str, state);
    798       break;
    799 #endif
    800 
    801 #ifdef STB_TEXTEDIT_MOVEWORDRIGHT
    802    case STB_TEXTEDIT_K_WORDRIGHT:
    803       if (STB_TEXT_HAS_SELECTION(state))
    804          stb_textedit_move_to_last(str, state);
    805       else {
    806          state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
    807          stb_textedit_clamp(str, state);
    808       }
    809       break;
    810 
    811    case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:
    812       if (!STB_TEXT_HAS_SELECTION(state))
    813          stb_textedit_prep_selection_at_cursor(state);
    814 
    815       state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
    816       state->select_end = state->cursor;
    817 
    818       stb_textedit_clamp(str, state);
    819       break;
    820 #endif
    821 
    822    case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
    823       stb_textedit_prep_selection_at_cursor(state);
    824       // move selection right
    825       ++state->select_end;
    826       stb_textedit_clamp(str, state);
    827       state->cursor = state->select_end;
    828       state->has_preferred_x = 0;
    829       break;
    830 
    831    case STB_TEXTEDIT_K_DOWN:
    832    case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: {
    833       StbFindState find;
    834       StbTexteditRow row;
    835       int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
    836 
    837       if (state->single_line) {
    838          // on windows, up&down in single-line behave like left&right
    839          key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
    840          goto retry;
    841       }
    842 
    843       if (sel)
    844          stb_textedit_prep_selection_at_cursor(state);
    845       else if (STB_TEXT_HAS_SELECTION(state))
    846          stb_textedit_move_to_last(str, state);
    847 
    848       // compute current position of cursor point
    849       stb_textedit_clamp(str, state);
    850       stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
    851 
    852       // now find character position down a row
    853       if (find.length) {
    854          float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
    855          float x;
    856          int start = find.first_char + find.length;
    857          state->cursor = start;
    858          STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
    859          x = row.x0;
    860          for (i = 0; i < row.num_chars; ++i) {
    861             float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
    862 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
    863             if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
    864                break;
    865 #endif
    866             x += dx;
    867             if (x > goal_x)
    868                break;
    869             ++state->cursor;
    870          }
    871          stb_textedit_clamp(str, state);
    872 
    873          state->has_preferred_x = 1;
    874          state->preferred_x = goal_x;
    875 
    876          if (sel)
    877             state->select_end = state->cursor;
    878       }
    879       break;
    880    }
    881 
    882    case STB_TEXTEDIT_K_UP:
    883    case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: {
    884       StbFindState find;
    885       StbTexteditRow row;
    886       int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
    887 
    888       if (state->single_line) {
    889          // on windows, up&down become left&right
    890          key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
    891          goto retry;
    892       }
    893 
    894       if (sel)
    895          stb_textedit_prep_selection_at_cursor(state);
    896       else if (STB_TEXT_HAS_SELECTION(state))
    897          stb_textedit_move_to_first(state);
    898 
    899       // compute current position of cursor point
    900       stb_textedit_clamp(str, state);
    901       stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
    902 
    903       // can only go up if there's a previous row
    904       if (find.prev_first != find.first_char) {
    905          // now find character position up a row
    906          float goal_x = state->has_preferred_x ? state->preferred_x : find.x;
    907          float x;
    908          state->cursor = find.prev_first;
    909          STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
    910          x = row.x0;
    911          for (i = 0; i < row.num_chars; ++i) {
    912             float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
    913 #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE
    914             if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)
    915                break;
    916 #endif
    917             x += dx;
    918             if (x > goal_x)
    919                break;
    920             ++state->cursor;
    921          }
    922          stb_textedit_clamp(str, state);
    923 
    924          state->has_preferred_x = 1;
    925          state->preferred_x = goal_x;
    926 
    927          if (sel)
    928             state->select_end = state->cursor;
    929       }
    930       break;
    931    }
    932 
    933    case STB_TEXTEDIT_K_DELETE:
    934    case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:
    935       if (STB_TEXT_HAS_SELECTION(state))
    936          stb_textedit_delete_selection(str, state);
    937       else {
    938          int n = STB_TEXTEDIT_STRINGLEN(str);
    939          if (state->cursor < n)
    940             stb_textedit_delete(str, state, state->cursor, 1);
    941       }
    942       state->has_preferred_x = 0;
    943       break;
    944 
    945    case STB_TEXTEDIT_K_BACKSPACE:
    946    case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:
    947       if (STB_TEXT_HAS_SELECTION(state))
    948          stb_textedit_delete_selection(str, state);
    949       else {
    950          stb_textedit_clamp(str, state);
    951          if (state->cursor > 0) {
    952             stb_textedit_delete(str, state, state->cursor - 1, 1);
    953             --state->cursor;
    954          }
    955       }
    956       state->has_preferred_x = 0;
    957       break;
    958 
    959 #ifdef STB_TEXTEDIT_K_TEXTSTART2
    960    case STB_TEXTEDIT_K_TEXTSTART2:
    961 #endif
    962    case STB_TEXTEDIT_K_TEXTSTART:
    963       state->cursor = state->select_start = state->select_end = 0;
    964       state->has_preferred_x = 0;
    965       break;
    966 
    967 #ifdef STB_TEXTEDIT_K_TEXTEND2
    968    case STB_TEXTEDIT_K_TEXTEND2:
    969 #endif
    970    case STB_TEXTEDIT_K_TEXTEND:
    971       state->cursor = STB_TEXTEDIT_STRINGLEN(str);
    972       state->select_start = state->select_end = 0;
    973       state->has_preferred_x = 0;
    974       break;
    975 
    976 #ifdef STB_TEXTEDIT_K_TEXTSTART2
    977    case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:
    978 #endif
    979    case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:
    980       stb_textedit_prep_selection_at_cursor(state);
    981       state->cursor = state->select_end = 0;
    982       state->has_preferred_x = 0;
    983       break;
    984 
    985 #ifdef STB_TEXTEDIT_K_TEXTEND2
    986    case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:
    987 #endif
    988    case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:
    989       stb_textedit_prep_selection_at_cursor(state);
    990       state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str);
    991       state->has_preferred_x = 0;
    992       break;
    993 
    994 
    995 #ifdef STB_TEXTEDIT_K_LINESTART2
    996    case STB_TEXTEDIT_K_LINESTART2:
    997 #endif
    998    case STB_TEXTEDIT_K_LINESTART:
    999       stb_textedit_clamp(str, state);
    1000       stb_textedit_move_to_first(state);
    1001       if (state->single_line)
    1002          state->cursor = 0;
    1003       else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor - 1) != STB_TEXTEDIT_NEWLINE)
    1004          --state->cursor;
    1005       state->has_preferred_x = 0;
    1006       break;
    1007 
    1008 #ifdef STB_TEXTEDIT_K_LINEEND2
    1009    case STB_TEXTEDIT_K_LINEEND2:
    1010 #endif
    1011    case STB_TEXTEDIT_K_LINEEND: {
    1012       int n = STB_TEXTEDIT_STRINGLEN(str);
    1013       stb_textedit_clamp(str, state);
    1014       stb_textedit_move_to_first(state);
    1015       if (state->single_line)
    1016          state->cursor = n;
    1017       else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
    1018          ++state->cursor;
    1019       state->has_preferred_x = 0;
    1020       break;
    1021    }
    1022 
    1023 #ifdef STB_TEXTEDIT_K_LINESTART2
    1024    case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:
    1025 #endif
    1026    case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
    1027       stb_textedit_clamp(str, state);
    1028       stb_textedit_prep_selection_at_cursor(state);
    1029       if (state->single_line)
    1030          state->cursor = 0;
    1031       else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor - 1) != STB_TEXTEDIT_NEWLINE)
    1032          --state->cursor;
    1033       state->select_end = state->cursor;
    1034       state->has_preferred_x = 0;
    1035       break;
    1036 
    1037 #ifdef STB_TEXTEDIT_K_LINEEND2
    1038    case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
    1039 #endif
    1040    case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
    1041       int n = STB_TEXTEDIT_STRINGLEN(str);
    1042       stb_textedit_clamp(str, state);
    1043       stb_textedit_prep_selection_at_cursor(state);
    1044       if (state->single_line)
    1045          state->cursor = n;
    1046       else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
    1047          ++state->cursor;
    1048       state->select_end = state->cursor;
    1049       state->has_preferred_x = 0;
    1050       break;
    1051    }
    1052 
    1053                                                        // @TODO:
    1054                                                        //    STB_TEXTEDIT_K_PGUP      - move cursor up a page
    1055                                                        //    STB_TEXTEDIT_K_PGDOWN    - move cursor down a page
    10561111   }
    10571112}
     
    10771132         int n = state->undo_rec[0].insert_length, i;
    10781133         // delete n characters from all other records
    1079          state->undo_char_point = state->undo_char_point - (short)n;  // vsnet05
    1080          STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t)((size_t)state->undo_char_point * sizeof(STB_TEXTEDIT_CHARTYPE)));
    1081          for (i = 0; i < state->undo_point; ++i)
     1134         state->undo_char_point -= n;
     1135         STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));
     1136         for (i=0; i < state->undo_point; ++i)
    10821137            if (state->undo_rec[i].char_storage >= 0)
    1083                state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short)n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it
     1138               state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
    10841139      }
    10851140      --state->undo_point;
    1086       STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec + 1, (size_t)((size_t)state->undo_point * sizeof(state->undo_rec[0])));
     1141      STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
    10871142   }
    10881143}
     
    10941149static void stb_textedit_discard_redo(StbUndoState *state)
    10951150{
    1096    int k = STB_TEXTEDIT_UNDOSTATECOUNT - 1;
     1151   int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;
    10971152
    10981153   if (state->redo_point <= k) {
     
    11001155      if (state->undo_rec[k].char_storage >= 0) {
    11011156         int n = state->undo_rec[k].insert_length, i;
    1102          // delete n characters from all other records
    1103          state->redo_char_point = state->redo_char_point + (short)n; // vsnet05
    1104          STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point - n, (size_t)((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point) * sizeof(STB_TEXTEDIT_CHARTYPE)));
    1105          for (i = state->redo_point; i < k; ++i)
     1157         // move the remaining redo character data to the end of the buffer
     1158         state->redo_char_point += n;
     1159         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)));
     1160         // adjust the position of all the other records to account for above memmove
     1161         for (i=state->redo_point; i < k; ++i)
    11061162            if (state->undo_rec[i].char_storage >= 0)
    1107                state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short)n; // vsnet05
     1163               state->undo_rec[i].char_storage += n;
    11081164      }
    1109       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])));
     1165      // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
     1166      // [DEAR IMGUI]
     1167      size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
     1168      const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
     1169      const char* buf_end   = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
     1170      IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
     1171      IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
     1172      STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
     1173
     1174      // now move redo_point to point to the new one
    11101175      ++state->redo_point;
    11111176   }
     
    11431208
    11441209   r->where = pos;
    1145    r->insert_length = (short)insert_len;
    1146    r->delete_length = (short)delete_len;
     1210   r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;
     1211   r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;
    11471212
    11481213   if (insert_len == 0) {
    11491214      r->char_storage = -1;
    11501215      return NULL;
    1151    }
    1152    else {
     1216   } else {
    11531217      r->char_storage = state->undo_char_point;
    1154       state->undo_char_point = state->undo_char_point + (short)insert_len;
     1218      state->undo_char_point += insert_len;
    11551219      return &state->undo_char[r->char_storage];
    11561220   }
     
    11651229
    11661230   // we need to do two things: apply the undo record, and create a redo record
    1167    u = s->undo_rec[s->undo_point - 1];
    1168    r = &s->undo_rec[s->redo_point - 1];
     1231   u = s->undo_rec[s->undo_point-1];
     1232   r = &s->undo_rec[s->redo_point-1];
    11691233   r->char_storage = -1;
    11701234
     
    11871251         // the undo records take up too much character space; there's no space to store the redo characters
    11881252         r->insert_length = 0;
    1189       }
    1190       else {
     1253      } else {
    11911254         int i;
    11921255
    11931256         // there's definitely room to store the characters eventually
    11941257         while (s->undo_char_point + u.delete_length > s->redo_char_point) {
    1195             // there's currently not enough room, so discard a redo record
    1196             stb_textedit_discard_redo(s);
    11971258            // should never happen:
    11981259            if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)
    11991260               return;
     1261            // there's currently not enough room, so discard a redo record
     1262            stb_textedit_discard_redo(s);
    12001263         }
    1201          r = &s->undo_rec[s->redo_point - 1];
     1264         r = &s->undo_rec[s->redo_point-1];
    12021265
    12031266         r->char_storage = s->redo_char_point - u.delete_length;
    1204          s->redo_char_point = s->redo_char_point - (short)u.delete_length;
     1267         s->redo_char_point = s->redo_char_point - u.delete_length;
    12051268
    12061269         // now save the characters
    1207          for (i = 0; i < u.delete_length; ++i)
     1270         for (i=0; i < u.delete_length; ++i)
    12081271            s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i);
    12091272      }
     
    12521315         u->insert_length = 0;
    12531316         u->delete_length = 0;
    1254       }
    1255       else {
     1317      } else {
    12561318         int i;
    12571319         u->char_storage = s->undo_char_point;
     
    12591321
    12601322         // now save the characters
    1261          for (i = 0; i < u->insert_length; ++i)
     1323         for (i=0; i < u->insert_length; ++i)
    12621324            s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i);
    12631325      }
     
    12881350   STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
    12891351   if (p) {
    1290       for (i = 0; i < length; ++i)
    1291          p[i] = STB_TEXTEDIT_GETCHAR(str, where + i);
     1352      for (i=0; i < length; ++i)
     1353         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
    12921354   }
    12931355}
     
    12981360   STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
    12991361   if (p) {
    1300       for (i = 0; i < old_length; ++i)
    1301          p[i] = STB_TEXTEDIT_GETCHAR(str, where + i);
     1362      for (i=0; i < old_length; ++i)
     1363         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
    13021364   }
    13031365}
     
    13161378   state->cursor_at_end_of_line = 0;
    13171379   state->initialized = 1;
    1318    state->single_line = (unsigned char)is_single_line;
     1380   state->single_line = (unsigned char) is_single_line;
    13191381   state->insert_mode = 0;
     1382   state->row_count_per_page = 0;
    13201383}
    13211384
     
    13251388   stb_textedit_clear_state(state, is_single_line);
    13261389}
     1390
     1391#if defined(__GNUC__) || defined(__clang__)
     1392#pragma GCC diagnostic push
     1393#pragma GCC diagnostic ignored "-Wcast-qual"
     1394#endif
     1395
     1396static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)
     1397{
     1398   return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);
     1399}
     1400
     1401#if defined(__GNUC__) || defined(__clang__)
     1402#pragma GCC diagnostic pop
     1403#endif
     1404
    13271405#endif//STB_TEXTEDIT_IMPLEMENTATION
     1406
     1407/*
     1408------------------------------------------------------------------------------
     1409This software is available under 2 licenses -- choose whichever you prefer.
     1410------------------------------------------------------------------------------
     1411ALTERNATIVE A - MIT License
     1412Copyright (c) 2017 Sean Barrett
     1413Permission is hereby granted, free of charge, to any person obtaining a copy of
     1414this software and associated documentation files (the "Software"), to deal in
     1415the Software without restriction, including without limitation the rights to
     1416use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     1417of the Software, and to permit persons to whom the Software is furnished to do
     1418so, subject to the following conditions:
     1419The above copyright notice and this permission notice shall be included in all
     1420copies or substantial portions of the Software.
     1421THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     1422IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     1423FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     1424AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     1425LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     1426OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     1427SOFTWARE.
     1428------------------------------------------------------------------------------
     1429ALTERNATIVE B - Public Domain (www.unlicense.org)
     1430This is free and unencumbered software released into the public domain.
     1431Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
     1432software, either in source code form or as a compiled binary, for any purpose,
     1433commercial or non-commercial, and by any means.
     1434In jurisdictions that recognize copyright laws, the author or authors of this
     1435software dedicate any and all copyright interest in the software to the public
     1436domain. We make this dedication for the benefit of the public at large and to
     1437the detriment of our heirs and successors. We intend this dedication to be an
     1438overt act of relinquishment in perpetuity of all present and future rights to
     1439this software under copyright law.
     1440THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     1441IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     1442FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     1443AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     1444ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     1445WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     1446------------------------------------------------------------------------------
     1447*/
Note: See TracChangeset for help on using the changeset viewer.