1bf215546Sopenharmony_ci// [DEAR IMGUI] 2bf215546Sopenharmony_ci// This is a slightly modified version of stb_textedit.h 1.13. 3bf215546Sopenharmony_ci// Those changes would need to be pushed into nothings/stb: 4bf215546Sopenharmony_ci// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) 5bf215546Sopenharmony_ci// Grep for [DEAR IMGUI] to find the changes. 6bf215546Sopenharmony_ci 7bf215546Sopenharmony_ci// stb_textedit.h - v1.13 - public domain - Sean Barrett 8bf215546Sopenharmony_ci// Development of this library was sponsored by RAD Game Tools 9bf215546Sopenharmony_ci// 10bf215546Sopenharmony_ci// This C header file implements the guts of a multi-line text-editing 11bf215546Sopenharmony_ci// widget; you implement display, word-wrapping, and low-level string 12bf215546Sopenharmony_ci// insertion/deletion, and stb_textedit will map user inputs into 13bf215546Sopenharmony_ci// insertions & deletions, plus updates to the cursor position, 14bf215546Sopenharmony_ci// selection state, and undo state. 15bf215546Sopenharmony_ci// 16bf215546Sopenharmony_ci// It is intended for use in games and other systems that need to build 17bf215546Sopenharmony_ci// their own custom widgets and which do not have heavy text-editing 18bf215546Sopenharmony_ci// requirements (this library is not recommended for use for editing large 19bf215546Sopenharmony_ci// texts, as its performance does not scale and it has limited undo). 20bf215546Sopenharmony_ci// 21bf215546Sopenharmony_ci// Non-trivial behaviors are modelled after Windows text controls. 22bf215546Sopenharmony_ci// 23bf215546Sopenharmony_ci// 24bf215546Sopenharmony_ci// LICENSE 25bf215546Sopenharmony_ci// 26bf215546Sopenharmony_ci// See end of file for license information. 27bf215546Sopenharmony_ci// 28bf215546Sopenharmony_ci// 29bf215546Sopenharmony_ci// DEPENDENCIES 30bf215546Sopenharmony_ci// 31bf215546Sopenharmony_ci// Uses the C runtime function 'memmove', which you can override 32bf215546Sopenharmony_ci// by defining STB_TEXTEDIT_memmove before the implementation. 33bf215546Sopenharmony_ci// Uses no other functions. Performs no runtime allocations. 34bf215546Sopenharmony_ci// 35bf215546Sopenharmony_ci// 36bf215546Sopenharmony_ci// VERSION HISTORY 37bf215546Sopenharmony_ci// 38bf215546Sopenharmony_ci// 1.13 (2019-02-07) fix bug in undo size management 39bf215546Sopenharmony_ci// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash 40bf215546Sopenharmony_ci// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield 41bf215546Sopenharmony_ci// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual 42bf215546Sopenharmony_ci// 1.9 (2016-08-27) customizable move-by-word 43bf215546Sopenharmony_ci// 1.8 (2016-04-02) better keyboard handling when mouse button is down 44bf215546Sopenharmony_ci// 1.7 (2015-09-13) change y range handling in case baseline is non-0 45bf215546Sopenharmony_ci// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove 46bf215546Sopenharmony_ci// 1.5 (2014-09-10) add support for secondary keys for OS X 47bf215546Sopenharmony_ci// 1.4 (2014-08-17) fix signed/unsigned warnings 48bf215546Sopenharmony_ci// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary 49bf215546Sopenharmony_ci// 1.2 (2014-05-27) fix some RAD types that had crept into the new code 50bf215546Sopenharmony_ci// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) 51bf215546Sopenharmony_ci// 1.0 (2012-07-26) improve documentation, initial public release 52bf215546Sopenharmony_ci// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode 53bf215546Sopenharmony_ci// 0.2 (2011-11-28) fixes to undo/redo 54bf215546Sopenharmony_ci// 0.1 (2010-07-08) initial version 55bf215546Sopenharmony_ci// 56bf215546Sopenharmony_ci// ADDITIONAL CONTRIBUTORS 57bf215546Sopenharmony_ci// 58bf215546Sopenharmony_ci// Ulf Winklemann: move-by-word in 1.1 59bf215546Sopenharmony_ci// Fabian Giesen: secondary key inputs in 1.5 60bf215546Sopenharmony_ci// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 61bf215546Sopenharmony_ci// 62bf215546Sopenharmony_ci// Bugfixes: 63bf215546Sopenharmony_ci// Scott Graham 64bf215546Sopenharmony_ci// Daniel Keller 65bf215546Sopenharmony_ci// Omar Cornut 66bf215546Sopenharmony_ci// Dan Thompson 67bf215546Sopenharmony_ci// 68bf215546Sopenharmony_ci// USAGE 69bf215546Sopenharmony_ci// 70bf215546Sopenharmony_ci// This file behaves differently depending on what symbols you define 71bf215546Sopenharmony_ci// before including it. 72bf215546Sopenharmony_ci// 73bf215546Sopenharmony_ci// 74bf215546Sopenharmony_ci// Header-file mode: 75bf215546Sopenharmony_ci// 76bf215546Sopenharmony_ci// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, 77bf215546Sopenharmony_ci// it will operate in "header file" mode. In this mode, it declares a 78bf215546Sopenharmony_ci// single public symbol, STB_TexteditState, which encapsulates the current 79bf215546Sopenharmony_ci// state of a text widget (except for the string, which you will store 80bf215546Sopenharmony_ci// separately). 81bf215546Sopenharmony_ci// 82bf215546Sopenharmony_ci// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a 83bf215546Sopenharmony_ci// primitive type that defines a single character (e.g. char, wchar_t, etc). 84bf215546Sopenharmony_ci// 85bf215546Sopenharmony_ci// To save space or increase undo-ability, you can optionally define the 86bf215546Sopenharmony_ci// following things that are used by the undo system: 87bf215546Sopenharmony_ci// 88bf215546Sopenharmony_ci// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position 89bf215546Sopenharmony_ci// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 90bf215546Sopenharmony_ci// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 91bf215546Sopenharmony_ci// 92bf215546Sopenharmony_ci// If you don't define these, they are set to permissive types and 93bf215546Sopenharmony_ci// moderate sizes. The undo system does no memory allocations, so 94bf215546Sopenharmony_ci// it grows STB_TexteditState by the worst-case storage which is (in bytes): 95bf215546Sopenharmony_ci// 96bf215546Sopenharmony_ci// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT 97bf215546Sopenharmony_ci// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT 98bf215546Sopenharmony_ci// 99bf215546Sopenharmony_ci// 100bf215546Sopenharmony_ci// Implementation mode: 101bf215546Sopenharmony_ci// 102bf215546Sopenharmony_ci// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it 103bf215546Sopenharmony_ci// will compile the implementation of the text edit widget, depending 104bf215546Sopenharmony_ci// on a large number of symbols which must be defined before the include. 105bf215546Sopenharmony_ci// 106bf215546Sopenharmony_ci// The implementation is defined only as static functions. You will then 107bf215546Sopenharmony_ci// need to provide your own APIs in the same file which will access the 108bf215546Sopenharmony_ci// static functions. 109bf215546Sopenharmony_ci// 110bf215546Sopenharmony_ci// The basic concept is that you provide a "string" object which 111bf215546Sopenharmony_ci// behaves like an array of characters. stb_textedit uses indices to 112bf215546Sopenharmony_ci// refer to positions in the string, implicitly representing positions 113bf215546Sopenharmony_ci// in the displayed textedit. This is true for both plain text and 114bf215546Sopenharmony_ci// rich text; even with rich text stb_truetype interacts with your 115bf215546Sopenharmony_ci// code as if there was an array of all the displayed characters. 116bf215546Sopenharmony_ci// 117bf215546Sopenharmony_ci// Symbols that must be the same in header-file and implementation mode: 118bf215546Sopenharmony_ci// 119bf215546Sopenharmony_ci// STB_TEXTEDIT_CHARTYPE the character type 120bf215546Sopenharmony_ci// STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position 121bf215546Sopenharmony_ci// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow 122bf215546Sopenharmony_ci// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer 123bf215546Sopenharmony_ci// 124bf215546Sopenharmony_ci// Symbols you must define for implementation mode: 125bf215546Sopenharmony_ci// 126bf215546Sopenharmony_ci// STB_TEXTEDIT_STRING the type of object representing a string being edited, 127bf215546Sopenharmony_ci// typically this is a wrapper object with other data you need 128bf215546Sopenharmony_ci// 129bf215546Sopenharmony_ci// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) 130bf215546Sopenharmony_ci// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters 131bf215546Sopenharmony_ci// starting from character #n (see discussion below) 132bf215546Sopenharmony_ci// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character 133bf215546Sopenharmony_ci// to the xpos of the i+1'th char for a line of characters 134bf215546Sopenharmony_ci// starting at character #n (i.e. accounts for kerning 135bf215546Sopenharmony_ci// with previous char) 136bf215546Sopenharmony_ci// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character 137bf215546Sopenharmony_ci// (return type is int, -1 means not valid to insert) 138bf215546Sopenharmony_ci// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based 139bf215546Sopenharmony_ci// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize 140bf215546Sopenharmony_ci// as manually wordwrapping for end-of-line positioning 141bf215546Sopenharmony_ci// 142bf215546Sopenharmony_ci// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i 143bf215546Sopenharmony_ci// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) 144bf215546Sopenharmony_ci// 145bf215546Sopenharmony_ci// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key 146bf215546Sopenharmony_ci// 147bf215546Sopenharmony_ci// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left 148bf215546Sopenharmony_ci// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right 149bf215546Sopenharmony_ci// STB_TEXTEDIT_K_UP keyboard input to move cursor up 150bf215546Sopenharmony_ci// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down 151bf215546Sopenharmony_ci// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME 152bf215546Sopenharmony_ci// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END 153bf215546Sopenharmony_ci// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME 154bf215546Sopenharmony_ci// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END 155bf215546Sopenharmony_ci// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor 156bf215546Sopenharmony_ci// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor 157bf215546Sopenharmony_ci// STB_TEXTEDIT_K_UNDO keyboard input to perform undo 158bf215546Sopenharmony_ci// STB_TEXTEDIT_K_REDO keyboard input to perform redo 159bf215546Sopenharmony_ci// 160bf215546Sopenharmony_ci// Optional: 161bf215546Sopenharmony_ci// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode 162bf215546Sopenharmony_ci// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), 163bf215546Sopenharmony_ci// required for default WORDLEFT/WORDRIGHT handlers 164bf215546Sopenharmony_ci// STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to 165bf215546Sopenharmony_ci// STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to 166bf215546Sopenharmony_ci// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT 167bf215546Sopenharmony_ci// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT 168bf215546Sopenharmony_ci// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line 169bf215546Sopenharmony_ci// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line 170bf215546Sopenharmony_ci// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text 171bf215546Sopenharmony_ci// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text 172bf215546Sopenharmony_ci// 173bf215546Sopenharmony_ci// Todo: 174bf215546Sopenharmony_ci// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page 175bf215546Sopenharmony_ci// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page 176bf215546Sopenharmony_ci// 177bf215546Sopenharmony_ci// Keyboard input must be encoded as a single integer value; e.g. a character code 178bf215546Sopenharmony_ci// and some bitflags that represent shift states. to simplify the interface, SHIFT must 179bf215546Sopenharmony_ci// be a bitflag, so we can test the shifted state of cursor movements to allow selection, 180bf215546Sopenharmony_ci// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. 181bf215546Sopenharmony_ci// 182bf215546Sopenharmony_ci// You can encode other things, such as CONTROL or ALT, in additional bits, and 183bf215546Sopenharmony_ci// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, 184bf215546Sopenharmony_ci// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN 185bf215546Sopenharmony_ci// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, 186bf215546Sopenharmony_ci// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the 187bf215546Sopenharmony_ci// API below. The control keys will only match WM_KEYDOWN events because of the 188bf215546Sopenharmony_ci// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN 189bf215546Sopenharmony_ci// bit so it only decodes WM_CHAR events. 190bf215546Sopenharmony_ci// 191bf215546Sopenharmony_ci// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed 192bf215546Sopenharmony_ci// row of characters assuming they start on the i'th character--the width and 193bf215546Sopenharmony_ci// the height and the number of characters consumed. This allows this library 194bf215546Sopenharmony_ci// to traverse the entire layout incrementally. You need to compute word-wrapping 195bf215546Sopenharmony_ci// here. 196bf215546Sopenharmony_ci// 197bf215546Sopenharmony_ci// Each textfield keeps its own insert mode state, which is not how normal 198bf215546Sopenharmony_ci// applications work. To keep an app-wide insert mode, update/copy the 199bf215546Sopenharmony_ci// "insert_mode" field of STB_TexteditState before/after calling API functions. 200bf215546Sopenharmony_ci// 201bf215546Sopenharmony_ci// API 202bf215546Sopenharmony_ci// 203bf215546Sopenharmony_ci// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 204bf215546Sopenharmony_ci// 205bf215546Sopenharmony_ci// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 206bf215546Sopenharmony_ci// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 207bf215546Sopenharmony_ci// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 208bf215546Sopenharmony_ci// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 209bf215546Sopenharmony_ci// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) 210bf215546Sopenharmony_ci// 211bf215546Sopenharmony_ci// Each of these functions potentially updates the string and updates the 212bf215546Sopenharmony_ci// state. 213bf215546Sopenharmony_ci// 214bf215546Sopenharmony_ci// initialize_state: 215bf215546Sopenharmony_ci// set the textedit state to a known good default state when initially 216bf215546Sopenharmony_ci// constructing the textedit. 217bf215546Sopenharmony_ci// 218bf215546Sopenharmony_ci// click: 219bf215546Sopenharmony_ci// call this with the mouse x,y on a mouse down; it will update the cursor 220bf215546Sopenharmony_ci// and reset the selection start/end to the cursor point. the x,y must 221bf215546Sopenharmony_ci// be relative to the text widget, with (0,0) being the top left. 222bf215546Sopenharmony_ci// 223bf215546Sopenharmony_ci// drag: 224bf215546Sopenharmony_ci// call this with the mouse x,y on a mouse drag/up; it will update the 225bf215546Sopenharmony_ci// cursor and the selection end point 226bf215546Sopenharmony_ci// 227bf215546Sopenharmony_ci// cut: 228bf215546Sopenharmony_ci// call this to delete the current selection; returns true if there was 229bf215546Sopenharmony_ci// one. you should FIRST copy the current selection to the system paste buffer. 230bf215546Sopenharmony_ci// (To copy, just copy the current selection out of the string yourself.) 231bf215546Sopenharmony_ci// 232bf215546Sopenharmony_ci// paste: 233bf215546Sopenharmony_ci// call this to paste text at the current cursor point or over the current 234bf215546Sopenharmony_ci// selection if there is one. 235bf215546Sopenharmony_ci// 236bf215546Sopenharmony_ci// key: 237bf215546Sopenharmony_ci// call this for keyboard inputs sent to the textfield. you can use it 238bf215546Sopenharmony_ci// for "key down" events or for "translated" key events. if you need to 239bf215546Sopenharmony_ci// do both (as in Win32), or distinguish Unicode characters from control 240bf215546Sopenharmony_ci// inputs, set a high bit to distinguish the two; then you can define the 241bf215546Sopenharmony_ci// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit 242bf215546Sopenharmony_ci// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is 243bf215546Sopenharmony_ci// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to 244bf215546Sopenharmony_ci// anything other type you wante before including. 245bf215546Sopenharmony_ci// 246bf215546Sopenharmony_ci// 247bf215546Sopenharmony_ci// When rendering, you can read the cursor position and selection state from 248bf215546Sopenharmony_ci// the STB_TexteditState. 249bf215546Sopenharmony_ci// 250bf215546Sopenharmony_ci// 251bf215546Sopenharmony_ci// Notes: 252bf215546Sopenharmony_ci// 253bf215546Sopenharmony_ci// This is designed to be usable in IMGUI, so it allows for the possibility of 254bf215546Sopenharmony_ci// running in an IMGUI that has NOT cached the multi-line layout. For this 255bf215546Sopenharmony_ci// reason, it provides an interface that is compatible with computing the 256bf215546Sopenharmony_ci// layout incrementally--we try to make sure we make as few passes through 257bf215546Sopenharmony_ci// as possible. (For example, to locate the mouse pointer in the text, we 258bf215546Sopenharmony_ci// could define functions that return the X and Y positions of characters 259bf215546Sopenharmony_ci// and binary search Y and then X, but if we're doing dynamic layout this 260bf215546Sopenharmony_ci// will run the layout algorithm many times, so instead we manually search 261bf215546Sopenharmony_ci// forward in one pass. Similar logic applies to e.g. up-arrow and 262bf215546Sopenharmony_ci// down-arrow movement.) 263bf215546Sopenharmony_ci// 264bf215546Sopenharmony_ci// If it's run in a widget that *has* cached the layout, then this is less 265bf215546Sopenharmony_ci// efficient, but it's not horrible on modern computers. But you wouldn't 266bf215546Sopenharmony_ci// want to edit million-line files with it. 267bf215546Sopenharmony_ci 268bf215546Sopenharmony_ci 269bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 270bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 271bf215546Sopenharmony_ci//// 272bf215546Sopenharmony_ci//// Header-file mode 273bf215546Sopenharmony_ci//// 274bf215546Sopenharmony_ci//// 275bf215546Sopenharmony_ci 276bf215546Sopenharmony_ci#ifndef INCLUDE_STB_TEXTEDIT_H 277bf215546Sopenharmony_ci#define INCLUDE_STB_TEXTEDIT_H 278bf215546Sopenharmony_ci 279bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////// 280bf215546Sopenharmony_ci// 281bf215546Sopenharmony_ci// STB_TexteditState 282bf215546Sopenharmony_ci// 283bf215546Sopenharmony_ci// Definition of STB_TexteditState which you should store 284bf215546Sopenharmony_ci// per-textfield; it includes cursor position, selection state, 285bf215546Sopenharmony_ci// and undo state. 286bf215546Sopenharmony_ci// 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_UNDOSTATECOUNT 289bf215546Sopenharmony_ci#define STB_TEXTEDIT_UNDOSTATECOUNT 99 290bf215546Sopenharmony_ci#endif 291bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_UNDOCHARCOUNT 292bf215546Sopenharmony_ci#define STB_TEXTEDIT_UNDOCHARCOUNT 999 293bf215546Sopenharmony_ci#endif 294bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_CHARTYPE 295bf215546Sopenharmony_ci#define STB_TEXTEDIT_CHARTYPE int 296bf215546Sopenharmony_ci#endif 297bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_POSITIONTYPE 298bf215546Sopenharmony_ci#define STB_TEXTEDIT_POSITIONTYPE int 299bf215546Sopenharmony_ci#endif 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_citypedef struct 302bf215546Sopenharmony_ci{ 303bf215546Sopenharmony_ci // private data 304bf215546Sopenharmony_ci STB_TEXTEDIT_POSITIONTYPE where; 305bf215546Sopenharmony_ci STB_TEXTEDIT_POSITIONTYPE insert_length; 306bf215546Sopenharmony_ci STB_TEXTEDIT_POSITIONTYPE delete_length; 307bf215546Sopenharmony_ci int char_storage; 308bf215546Sopenharmony_ci} StbUndoRecord; 309bf215546Sopenharmony_ci 310bf215546Sopenharmony_citypedef struct 311bf215546Sopenharmony_ci{ 312bf215546Sopenharmony_ci // private data 313bf215546Sopenharmony_ci StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; 314bf215546Sopenharmony_ci STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; 315bf215546Sopenharmony_ci short undo_point, redo_point; 316bf215546Sopenharmony_ci int undo_char_point, redo_char_point; 317bf215546Sopenharmony_ci} StbUndoState; 318bf215546Sopenharmony_ci 319bf215546Sopenharmony_citypedef struct 320bf215546Sopenharmony_ci{ 321bf215546Sopenharmony_ci ///////////////////// 322bf215546Sopenharmony_ci // 323bf215546Sopenharmony_ci // public data 324bf215546Sopenharmony_ci // 325bf215546Sopenharmony_ci 326bf215546Sopenharmony_ci int cursor; 327bf215546Sopenharmony_ci // position of the text cursor within the string 328bf215546Sopenharmony_ci 329bf215546Sopenharmony_ci int select_start; // selection start point 330bf215546Sopenharmony_ci int select_end; 331bf215546Sopenharmony_ci // selection start and end point in characters; if equal, no selection. 332bf215546Sopenharmony_ci // note that start may be less than or greater than end (e.g. when 333bf215546Sopenharmony_ci // dragging the mouse, start is where the initial click was, and you 334bf215546Sopenharmony_ci // can drag in either direction) 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci unsigned char insert_mode; 337bf215546Sopenharmony_ci // each textfield keeps its own insert mode state. to keep an app-wide 338bf215546Sopenharmony_ci // insert mode, copy this value in/out of the app state 339bf215546Sopenharmony_ci 340bf215546Sopenharmony_ci ///////////////////// 341bf215546Sopenharmony_ci // 342bf215546Sopenharmony_ci // private data 343bf215546Sopenharmony_ci // 344bf215546Sopenharmony_ci unsigned char cursor_at_end_of_line; // not implemented yet 345bf215546Sopenharmony_ci unsigned char initialized; 346bf215546Sopenharmony_ci unsigned char has_preferred_x; 347bf215546Sopenharmony_ci unsigned char single_line; 348bf215546Sopenharmony_ci unsigned char padding1, padding2, padding3; 349bf215546Sopenharmony_ci float preferred_x; // this determines where the cursor up/down tries to seek to along x 350bf215546Sopenharmony_ci StbUndoState undostate; 351bf215546Sopenharmony_ci} STB_TexteditState; 352bf215546Sopenharmony_ci 353bf215546Sopenharmony_ci 354bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////// 355bf215546Sopenharmony_ci// 356bf215546Sopenharmony_ci// StbTexteditRow 357bf215546Sopenharmony_ci// 358bf215546Sopenharmony_ci// Result of layout query, used by stb_textedit to determine where 359bf215546Sopenharmony_ci// the text in each row is. 360bf215546Sopenharmony_ci 361bf215546Sopenharmony_ci// result of layout query 362bf215546Sopenharmony_citypedef struct 363bf215546Sopenharmony_ci{ 364bf215546Sopenharmony_ci float x0,x1; // starting x location, end x location (allows for align=right, etc) 365bf215546Sopenharmony_ci float baseline_y_delta; // position of baseline relative to previous row's baseline 366bf215546Sopenharmony_ci float ymin,ymax; // height of row above and below baseline 367bf215546Sopenharmony_ci int num_chars; 368bf215546Sopenharmony_ci} StbTexteditRow; 369bf215546Sopenharmony_ci#endif //INCLUDE_STB_TEXTEDIT_H 370bf215546Sopenharmony_ci 371bf215546Sopenharmony_ci 372bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 373bf215546Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 374bf215546Sopenharmony_ci//// 375bf215546Sopenharmony_ci//// Implementation mode 376bf215546Sopenharmony_ci//// 377bf215546Sopenharmony_ci//// 378bf215546Sopenharmony_ci 379bf215546Sopenharmony_ci 380bf215546Sopenharmony_ci// implementation isn't include-guarded, since it might have indirectly 381bf215546Sopenharmony_ci// included just the "header" portion 382bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_IMPLEMENTATION 383bf215546Sopenharmony_ci 384bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_memmove 385bf215546Sopenharmony_ci#include <string.h> 386bf215546Sopenharmony_ci#define STB_TEXTEDIT_memmove memmove 387bf215546Sopenharmony_ci#endif 388bf215546Sopenharmony_ci 389bf215546Sopenharmony_ci 390bf215546Sopenharmony_ci///////////////////////////////////////////////////////////////////////////// 391bf215546Sopenharmony_ci// 392bf215546Sopenharmony_ci// Mouse input handling 393bf215546Sopenharmony_ci// 394bf215546Sopenharmony_ci 395bf215546Sopenharmony_ci// traverse the layout to locate the nearest character to a display position 396bf215546Sopenharmony_cistatic int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) 397bf215546Sopenharmony_ci{ 398bf215546Sopenharmony_ci StbTexteditRow r; 399bf215546Sopenharmony_ci int n = STB_TEXTEDIT_STRINGLEN(str); 400bf215546Sopenharmony_ci float base_y = 0, prev_x; 401bf215546Sopenharmony_ci int i=0, k; 402bf215546Sopenharmony_ci 403bf215546Sopenharmony_ci r.x0 = r.x1 = 0; 404bf215546Sopenharmony_ci r.ymin = r.ymax = 0; 405bf215546Sopenharmony_ci r.num_chars = 0; 406bf215546Sopenharmony_ci 407bf215546Sopenharmony_ci // search rows to find one that straddles 'y' 408bf215546Sopenharmony_ci while (i < n) { 409bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, i); 410bf215546Sopenharmony_ci if (r.num_chars <= 0) 411bf215546Sopenharmony_ci return n; 412bf215546Sopenharmony_ci 413bf215546Sopenharmony_ci if (i==0 && y < base_y + r.ymin) 414bf215546Sopenharmony_ci return 0; 415bf215546Sopenharmony_ci 416bf215546Sopenharmony_ci if (y < base_y + r.ymax) 417bf215546Sopenharmony_ci break; 418bf215546Sopenharmony_ci 419bf215546Sopenharmony_ci i += r.num_chars; 420bf215546Sopenharmony_ci base_y += r.baseline_y_delta; 421bf215546Sopenharmony_ci } 422bf215546Sopenharmony_ci 423bf215546Sopenharmony_ci // below all text, return 'after' last character 424bf215546Sopenharmony_ci if (i >= n) 425bf215546Sopenharmony_ci return n; 426bf215546Sopenharmony_ci 427bf215546Sopenharmony_ci // check if it's before the beginning of the line 428bf215546Sopenharmony_ci if (x < r.x0) 429bf215546Sopenharmony_ci return i; 430bf215546Sopenharmony_ci 431bf215546Sopenharmony_ci // check if it's before the end of the line 432bf215546Sopenharmony_ci if (x < r.x1) { 433bf215546Sopenharmony_ci // search characters in row for one that straddles 'x' 434bf215546Sopenharmony_ci prev_x = r.x0; 435bf215546Sopenharmony_ci for (k=0; k < r.num_chars; ++k) { 436bf215546Sopenharmony_ci float w = STB_TEXTEDIT_GETWIDTH(str, i, k); 437bf215546Sopenharmony_ci if (x < prev_x+w) { 438bf215546Sopenharmony_ci if (x < prev_x+w/2) 439bf215546Sopenharmony_ci return k+i; 440bf215546Sopenharmony_ci else 441bf215546Sopenharmony_ci return k+i+1; 442bf215546Sopenharmony_ci } 443bf215546Sopenharmony_ci prev_x += w; 444bf215546Sopenharmony_ci } 445bf215546Sopenharmony_ci // shouldn't happen, but if it does, fall through to end-of-line case 446bf215546Sopenharmony_ci } 447bf215546Sopenharmony_ci 448bf215546Sopenharmony_ci // if the last character is a newline, return that. otherwise return 'after' the last character 449bf215546Sopenharmony_ci if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) 450bf215546Sopenharmony_ci return i+r.num_chars-1; 451bf215546Sopenharmony_ci else 452bf215546Sopenharmony_ci return i+r.num_chars; 453bf215546Sopenharmony_ci} 454bf215546Sopenharmony_ci 455bf215546Sopenharmony_ci// API click: on mouse down, move the cursor to the clicked location, and reset the selection 456bf215546Sopenharmony_cistatic void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 457bf215546Sopenharmony_ci{ 458bf215546Sopenharmony_ci // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse 459bf215546Sopenharmony_ci // goes off the top or bottom of the text 460bf215546Sopenharmony_ci if( state->single_line ) 461bf215546Sopenharmony_ci { 462bf215546Sopenharmony_ci StbTexteditRow r; 463bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 464bf215546Sopenharmony_ci y = r.ymin; 465bf215546Sopenharmony_ci } 466bf215546Sopenharmony_ci 467bf215546Sopenharmony_ci state->cursor = stb_text_locate_coord(str, x, y); 468bf215546Sopenharmony_ci state->select_start = state->cursor; 469bf215546Sopenharmony_ci state->select_end = state->cursor; 470bf215546Sopenharmony_ci state->has_preferred_x = 0; 471bf215546Sopenharmony_ci} 472bf215546Sopenharmony_ci 473bf215546Sopenharmony_ci// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location 474bf215546Sopenharmony_cistatic void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) 475bf215546Sopenharmony_ci{ 476bf215546Sopenharmony_ci int p = 0; 477bf215546Sopenharmony_ci 478bf215546Sopenharmony_ci // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse 479bf215546Sopenharmony_ci // goes off the top or bottom of the text 480bf215546Sopenharmony_ci if( state->single_line ) 481bf215546Sopenharmony_ci { 482bf215546Sopenharmony_ci StbTexteditRow r; 483bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 484bf215546Sopenharmony_ci y = r.ymin; 485bf215546Sopenharmony_ci } 486bf215546Sopenharmony_ci 487bf215546Sopenharmony_ci if (state->select_start == state->select_end) 488bf215546Sopenharmony_ci state->select_start = state->cursor; 489bf215546Sopenharmony_ci 490bf215546Sopenharmony_ci p = stb_text_locate_coord(str, x, y); 491bf215546Sopenharmony_ci state->cursor = state->select_end = p; 492bf215546Sopenharmony_ci} 493bf215546Sopenharmony_ci 494bf215546Sopenharmony_ci///////////////////////////////////////////////////////////////////////////// 495bf215546Sopenharmony_ci// 496bf215546Sopenharmony_ci// Keyboard input handling 497bf215546Sopenharmony_ci// 498bf215546Sopenharmony_ci 499bf215546Sopenharmony_ci// forward declarations 500bf215546Sopenharmony_cistatic void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 501bf215546Sopenharmony_cistatic void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); 502bf215546Sopenharmony_cistatic void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); 503bf215546Sopenharmony_cistatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); 504bf215546Sopenharmony_cistatic void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); 505bf215546Sopenharmony_ci 506bf215546Sopenharmony_citypedef struct 507bf215546Sopenharmony_ci{ 508bf215546Sopenharmony_ci float x,y; // position of n'th character 509bf215546Sopenharmony_ci float height; // height of line 510bf215546Sopenharmony_ci int first_char, length; // first char of row, and length 511bf215546Sopenharmony_ci int prev_first; // first char of previous row 512bf215546Sopenharmony_ci} StbFindState; 513bf215546Sopenharmony_ci 514bf215546Sopenharmony_ci// find the x/y location of a character, and remember info about the previous row in 515bf215546Sopenharmony_ci// case we get a move-up event (for page up, we'll have to rescan) 516bf215546Sopenharmony_cistatic void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) 517bf215546Sopenharmony_ci{ 518bf215546Sopenharmony_ci StbTexteditRow r; 519bf215546Sopenharmony_ci int prev_start = 0; 520bf215546Sopenharmony_ci int z = STB_TEXTEDIT_STRINGLEN(str); 521bf215546Sopenharmony_ci int i=0, first; 522bf215546Sopenharmony_ci 523bf215546Sopenharmony_ci if (n == z) { 524bf215546Sopenharmony_ci // if it's at the end, then find the last line -- simpler than trying to 525bf215546Sopenharmony_ci // explicitly handle this case in the regular code 526bf215546Sopenharmony_ci if (single_line) { 527bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, 0); 528bf215546Sopenharmony_ci find->y = 0; 529bf215546Sopenharmony_ci find->first_char = 0; 530bf215546Sopenharmony_ci find->length = z; 531bf215546Sopenharmony_ci find->height = r.ymax - r.ymin; 532bf215546Sopenharmony_ci find->x = r.x1; 533bf215546Sopenharmony_ci } else { 534bf215546Sopenharmony_ci find->y = 0; 535bf215546Sopenharmony_ci find->x = 0; 536bf215546Sopenharmony_ci find->height = 1; 537bf215546Sopenharmony_ci while (i < z) { 538bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, i); 539bf215546Sopenharmony_ci prev_start = i; 540bf215546Sopenharmony_ci i += r.num_chars; 541bf215546Sopenharmony_ci } 542bf215546Sopenharmony_ci find->first_char = i; 543bf215546Sopenharmony_ci find->length = 0; 544bf215546Sopenharmony_ci find->prev_first = prev_start; 545bf215546Sopenharmony_ci } 546bf215546Sopenharmony_ci return; 547bf215546Sopenharmony_ci } 548bf215546Sopenharmony_ci 549bf215546Sopenharmony_ci // search rows to find the one that straddles character n 550bf215546Sopenharmony_ci find->y = 0; 551bf215546Sopenharmony_ci 552bf215546Sopenharmony_ci for(;;) { 553bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&r, str, i); 554bf215546Sopenharmony_ci if (n < i + r.num_chars) 555bf215546Sopenharmony_ci break; 556bf215546Sopenharmony_ci prev_start = i; 557bf215546Sopenharmony_ci i += r.num_chars; 558bf215546Sopenharmony_ci find->y += r.baseline_y_delta; 559bf215546Sopenharmony_ci } 560bf215546Sopenharmony_ci 561bf215546Sopenharmony_ci find->first_char = first = i; 562bf215546Sopenharmony_ci find->length = r.num_chars; 563bf215546Sopenharmony_ci find->height = r.ymax - r.ymin; 564bf215546Sopenharmony_ci find->prev_first = prev_start; 565bf215546Sopenharmony_ci 566bf215546Sopenharmony_ci // now scan to find xpos 567bf215546Sopenharmony_ci find->x = r.x0; 568bf215546Sopenharmony_ci for (i=0; first+i < n; ++i) 569bf215546Sopenharmony_ci find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); 570bf215546Sopenharmony_ci} 571bf215546Sopenharmony_ci 572bf215546Sopenharmony_ci#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) 573bf215546Sopenharmony_ci 574bf215546Sopenharmony_ci// make the selection/cursor state valid if client altered the string 575bf215546Sopenharmony_cistatic void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 576bf215546Sopenharmony_ci{ 577bf215546Sopenharmony_ci int n = STB_TEXTEDIT_STRINGLEN(str); 578bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) { 579bf215546Sopenharmony_ci if (state->select_start > n) state->select_start = n; 580bf215546Sopenharmony_ci if (state->select_end > n) state->select_end = n; 581bf215546Sopenharmony_ci // if clamping forced them to be equal, move the cursor to match 582bf215546Sopenharmony_ci if (state->select_start == state->select_end) 583bf215546Sopenharmony_ci state->cursor = state->select_start; 584bf215546Sopenharmony_ci } 585bf215546Sopenharmony_ci if (state->cursor > n) state->cursor = n; 586bf215546Sopenharmony_ci} 587bf215546Sopenharmony_ci 588bf215546Sopenharmony_ci// delete characters while updating undo 589bf215546Sopenharmony_cistatic void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) 590bf215546Sopenharmony_ci{ 591bf215546Sopenharmony_ci stb_text_makeundo_delete(str, state, where, len); 592bf215546Sopenharmony_ci STB_TEXTEDIT_DELETECHARS(str, where, len); 593bf215546Sopenharmony_ci state->has_preferred_x = 0; 594bf215546Sopenharmony_ci} 595bf215546Sopenharmony_ci 596bf215546Sopenharmony_ci// delete the section 597bf215546Sopenharmony_cistatic void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 598bf215546Sopenharmony_ci{ 599bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 600bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) { 601bf215546Sopenharmony_ci if (state->select_start < state->select_end) { 602bf215546Sopenharmony_ci stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); 603bf215546Sopenharmony_ci state->select_end = state->cursor = state->select_start; 604bf215546Sopenharmony_ci } else { 605bf215546Sopenharmony_ci stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); 606bf215546Sopenharmony_ci state->select_start = state->cursor = state->select_end; 607bf215546Sopenharmony_ci } 608bf215546Sopenharmony_ci state->has_preferred_x = 0; 609bf215546Sopenharmony_ci } 610bf215546Sopenharmony_ci} 611bf215546Sopenharmony_ci 612bf215546Sopenharmony_ci// canoncialize the selection so start <= end 613bf215546Sopenharmony_cistatic void stb_textedit_sortselection(STB_TexteditState *state) 614bf215546Sopenharmony_ci{ 615bf215546Sopenharmony_ci if (state->select_end < state->select_start) { 616bf215546Sopenharmony_ci int temp = state->select_end; 617bf215546Sopenharmony_ci state->select_end = state->select_start; 618bf215546Sopenharmony_ci state->select_start = temp; 619bf215546Sopenharmony_ci } 620bf215546Sopenharmony_ci} 621bf215546Sopenharmony_ci 622bf215546Sopenharmony_ci// move cursor to first character of selection 623bf215546Sopenharmony_cistatic void stb_textedit_move_to_first(STB_TexteditState *state) 624bf215546Sopenharmony_ci{ 625bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) { 626bf215546Sopenharmony_ci stb_textedit_sortselection(state); 627bf215546Sopenharmony_ci state->cursor = state->select_start; 628bf215546Sopenharmony_ci state->select_end = state->select_start; 629bf215546Sopenharmony_ci state->has_preferred_x = 0; 630bf215546Sopenharmony_ci } 631bf215546Sopenharmony_ci} 632bf215546Sopenharmony_ci 633bf215546Sopenharmony_ci// move cursor to last character of selection 634bf215546Sopenharmony_cistatic void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 635bf215546Sopenharmony_ci{ 636bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) { 637bf215546Sopenharmony_ci stb_textedit_sortselection(state); 638bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 639bf215546Sopenharmony_ci state->cursor = state->select_end; 640bf215546Sopenharmony_ci state->select_start = state->select_end; 641bf215546Sopenharmony_ci state->has_preferred_x = 0; 642bf215546Sopenharmony_ci } 643bf215546Sopenharmony_ci} 644bf215546Sopenharmony_ci 645bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_IS_SPACE 646bf215546Sopenharmony_cistatic int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) 647bf215546Sopenharmony_ci{ 648bf215546Sopenharmony_ci return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; 649bf215546Sopenharmony_ci} 650bf215546Sopenharmony_ci 651bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_MOVEWORDLEFT 652bf215546Sopenharmony_cistatic int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) 653bf215546Sopenharmony_ci{ 654bf215546Sopenharmony_ci --c; // always move at least one character 655bf215546Sopenharmony_ci while( c >= 0 && !is_word_boundary( str, c ) ) 656bf215546Sopenharmony_ci --c; 657bf215546Sopenharmony_ci 658bf215546Sopenharmony_ci if( c < 0 ) 659bf215546Sopenharmony_ci c = 0; 660bf215546Sopenharmony_ci 661bf215546Sopenharmony_ci return c; 662bf215546Sopenharmony_ci} 663bf215546Sopenharmony_ci#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous 664bf215546Sopenharmony_ci#endif 665bf215546Sopenharmony_ci 666bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_MOVEWORDRIGHT 667bf215546Sopenharmony_cistatic int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) 668bf215546Sopenharmony_ci{ 669bf215546Sopenharmony_ci const int len = STB_TEXTEDIT_STRINGLEN(str); 670bf215546Sopenharmony_ci ++c; // always move at least one character 671bf215546Sopenharmony_ci while( c < len && !is_word_boundary( str, c ) ) 672bf215546Sopenharmony_ci ++c; 673bf215546Sopenharmony_ci 674bf215546Sopenharmony_ci if( c > len ) 675bf215546Sopenharmony_ci c = len; 676bf215546Sopenharmony_ci 677bf215546Sopenharmony_ci return c; 678bf215546Sopenharmony_ci} 679bf215546Sopenharmony_ci#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next 680bf215546Sopenharmony_ci#endif 681bf215546Sopenharmony_ci 682bf215546Sopenharmony_ci#endif 683bf215546Sopenharmony_ci 684bf215546Sopenharmony_ci// update selection and cursor to match each other 685bf215546Sopenharmony_cistatic void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) 686bf215546Sopenharmony_ci{ 687bf215546Sopenharmony_ci if (!STB_TEXT_HAS_SELECTION(state)) 688bf215546Sopenharmony_ci state->select_start = state->select_end = state->cursor; 689bf215546Sopenharmony_ci else 690bf215546Sopenharmony_ci state->cursor = state->select_end; 691bf215546Sopenharmony_ci} 692bf215546Sopenharmony_ci 693bf215546Sopenharmony_ci// API cut: delete selection 694bf215546Sopenharmony_cistatic int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 695bf215546Sopenharmony_ci{ 696bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) { 697bf215546Sopenharmony_ci stb_textedit_delete_selection(str,state); // implicitly clamps 698bf215546Sopenharmony_ci state->has_preferred_x = 0; 699bf215546Sopenharmony_ci return 1; 700bf215546Sopenharmony_ci } 701bf215546Sopenharmony_ci return 0; 702bf215546Sopenharmony_ci} 703bf215546Sopenharmony_ci 704bf215546Sopenharmony_ci// API paste: replace existing selection with passed-in text 705bf215546Sopenharmony_cistatic int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) 706bf215546Sopenharmony_ci{ 707bf215546Sopenharmony_ci // if there's a selection, the paste should delete it 708bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 709bf215546Sopenharmony_ci stb_textedit_delete_selection(str,state); 710bf215546Sopenharmony_ci // try to insert the characters 711bf215546Sopenharmony_ci if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { 712bf215546Sopenharmony_ci stb_text_makeundo_insert(state, state->cursor, len); 713bf215546Sopenharmony_ci state->cursor += len; 714bf215546Sopenharmony_ci state->has_preferred_x = 0; 715bf215546Sopenharmony_ci return 1; 716bf215546Sopenharmony_ci } 717bf215546Sopenharmony_ci // remove the undo since we didn't actually insert the characters 718bf215546Sopenharmony_ci if (state->undostate.undo_point) 719bf215546Sopenharmony_ci --state->undostate.undo_point; 720bf215546Sopenharmony_ci return 0; 721bf215546Sopenharmony_ci} 722bf215546Sopenharmony_ci 723bf215546Sopenharmony_ci#ifndef STB_TEXTEDIT_KEYTYPE 724bf215546Sopenharmony_ci#define STB_TEXTEDIT_KEYTYPE int 725bf215546Sopenharmony_ci#endif 726bf215546Sopenharmony_ci 727bf215546Sopenharmony_ci// API key: process a keyboard input 728bf215546Sopenharmony_cistatic void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) 729bf215546Sopenharmony_ci{ 730bf215546Sopenharmony_ciretry: 731bf215546Sopenharmony_ci switch (key) { 732bf215546Sopenharmony_ci default: { 733bf215546Sopenharmony_ci int c = STB_TEXTEDIT_KEYTOTEXT(key); 734bf215546Sopenharmony_ci if (c > 0) { 735bf215546Sopenharmony_ci STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; 736bf215546Sopenharmony_ci 737bf215546Sopenharmony_ci // can't add newline in single-line mode 738bf215546Sopenharmony_ci if (c == '\n' && state->single_line) 739bf215546Sopenharmony_ci break; 740bf215546Sopenharmony_ci 741bf215546Sopenharmony_ci if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { 742bf215546Sopenharmony_ci stb_text_makeundo_replace(str, state, state->cursor, 1, 1); 743bf215546Sopenharmony_ci STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); 744bf215546Sopenharmony_ci if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 745bf215546Sopenharmony_ci ++state->cursor; 746bf215546Sopenharmony_ci state->has_preferred_x = 0; 747bf215546Sopenharmony_ci } 748bf215546Sopenharmony_ci } else { 749bf215546Sopenharmony_ci stb_textedit_delete_selection(str,state); // implicitly clamps 750bf215546Sopenharmony_ci if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { 751bf215546Sopenharmony_ci stb_text_makeundo_insert(state, state->cursor, 1); 752bf215546Sopenharmony_ci ++state->cursor; 753bf215546Sopenharmony_ci state->has_preferred_x = 0; 754bf215546Sopenharmony_ci } 755bf215546Sopenharmony_ci } 756bf215546Sopenharmony_ci } 757bf215546Sopenharmony_ci break; 758bf215546Sopenharmony_ci } 759bf215546Sopenharmony_ci 760bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_INSERT 761bf215546Sopenharmony_ci case STB_TEXTEDIT_K_INSERT: 762bf215546Sopenharmony_ci state->insert_mode = !state->insert_mode; 763bf215546Sopenharmony_ci break; 764bf215546Sopenharmony_ci#endif 765bf215546Sopenharmony_ci 766bf215546Sopenharmony_ci case STB_TEXTEDIT_K_UNDO: 767bf215546Sopenharmony_ci stb_text_undo(str, state); 768bf215546Sopenharmony_ci state->has_preferred_x = 0; 769bf215546Sopenharmony_ci break; 770bf215546Sopenharmony_ci 771bf215546Sopenharmony_ci case STB_TEXTEDIT_K_REDO: 772bf215546Sopenharmony_ci stb_text_redo(str, state); 773bf215546Sopenharmony_ci state->has_preferred_x = 0; 774bf215546Sopenharmony_ci break; 775bf215546Sopenharmony_ci 776bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LEFT: 777bf215546Sopenharmony_ci // if currently there's a selection, move cursor to start of selection 778bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 779bf215546Sopenharmony_ci stb_textedit_move_to_first(state); 780bf215546Sopenharmony_ci else 781bf215546Sopenharmony_ci if (state->cursor > 0) 782bf215546Sopenharmony_ci --state->cursor; 783bf215546Sopenharmony_ci state->has_preferred_x = 0; 784bf215546Sopenharmony_ci break; 785bf215546Sopenharmony_ci 786bf215546Sopenharmony_ci case STB_TEXTEDIT_K_RIGHT: 787bf215546Sopenharmony_ci // if currently there's a selection, move cursor to end of selection 788bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 789bf215546Sopenharmony_ci stb_textedit_move_to_last(str, state); 790bf215546Sopenharmony_ci else 791bf215546Sopenharmony_ci ++state->cursor; 792bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 793bf215546Sopenharmony_ci state->has_preferred_x = 0; 794bf215546Sopenharmony_ci break; 795bf215546Sopenharmony_ci 796bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: 797bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 798bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 799bf215546Sopenharmony_ci // move selection left 800bf215546Sopenharmony_ci if (state->select_end > 0) 801bf215546Sopenharmony_ci --state->select_end; 802bf215546Sopenharmony_ci state->cursor = state->select_end; 803bf215546Sopenharmony_ci state->has_preferred_x = 0; 804bf215546Sopenharmony_ci break; 805bf215546Sopenharmony_ci 806bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_MOVEWORDLEFT 807bf215546Sopenharmony_ci case STB_TEXTEDIT_K_WORDLEFT: 808bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 809bf215546Sopenharmony_ci stb_textedit_move_to_first(state); 810bf215546Sopenharmony_ci else { 811bf215546Sopenharmony_ci state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 812bf215546Sopenharmony_ci stb_textedit_clamp( str, state ); 813bf215546Sopenharmony_ci } 814bf215546Sopenharmony_ci break; 815bf215546Sopenharmony_ci 816bf215546Sopenharmony_ci case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: 817bf215546Sopenharmony_ci if( !STB_TEXT_HAS_SELECTION( state ) ) 818bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 819bf215546Sopenharmony_ci 820bf215546Sopenharmony_ci state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); 821bf215546Sopenharmony_ci state->select_end = state->cursor; 822bf215546Sopenharmony_ci 823bf215546Sopenharmony_ci stb_textedit_clamp( str, state ); 824bf215546Sopenharmony_ci break; 825bf215546Sopenharmony_ci#endif 826bf215546Sopenharmony_ci 827bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_MOVEWORDRIGHT 828bf215546Sopenharmony_ci case STB_TEXTEDIT_K_WORDRIGHT: 829bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 830bf215546Sopenharmony_ci stb_textedit_move_to_last(str, state); 831bf215546Sopenharmony_ci else { 832bf215546Sopenharmony_ci state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 833bf215546Sopenharmony_ci stb_textedit_clamp( str, state ); 834bf215546Sopenharmony_ci } 835bf215546Sopenharmony_ci break; 836bf215546Sopenharmony_ci 837bf215546Sopenharmony_ci case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: 838bf215546Sopenharmony_ci if( !STB_TEXT_HAS_SELECTION( state ) ) 839bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 840bf215546Sopenharmony_ci 841bf215546Sopenharmony_ci state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); 842bf215546Sopenharmony_ci state->select_end = state->cursor; 843bf215546Sopenharmony_ci 844bf215546Sopenharmony_ci stb_textedit_clamp( str, state ); 845bf215546Sopenharmony_ci break; 846bf215546Sopenharmony_ci#endif 847bf215546Sopenharmony_ci 848bf215546Sopenharmony_ci case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: 849bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 850bf215546Sopenharmony_ci // move selection right 851bf215546Sopenharmony_ci ++state->select_end; 852bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 853bf215546Sopenharmony_ci state->cursor = state->select_end; 854bf215546Sopenharmony_ci state->has_preferred_x = 0; 855bf215546Sopenharmony_ci break; 856bf215546Sopenharmony_ci 857bf215546Sopenharmony_ci case STB_TEXTEDIT_K_DOWN: 858bf215546Sopenharmony_ci case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { 859bf215546Sopenharmony_ci StbFindState find; 860bf215546Sopenharmony_ci StbTexteditRow row; 861bf215546Sopenharmony_ci int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 862bf215546Sopenharmony_ci 863bf215546Sopenharmony_ci if (state->single_line) { 864bf215546Sopenharmony_ci // on windows, up&down in single-line behave like left&right 865bf215546Sopenharmony_ci key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); 866bf215546Sopenharmony_ci goto retry; 867bf215546Sopenharmony_ci } 868bf215546Sopenharmony_ci 869bf215546Sopenharmony_ci if (sel) 870bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 871bf215546Sopenharmony_ci else if (STB_TEXT_HAS_SELECTION(state)) 872bf215546Sopenharmony_ci stb_textedit_move_to_last(str,state); 873bf215546Sopenharmony_ci 874bf215546Sopenharmony_ci // compute current position of cursor point 875bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 876bf215546Sopenharmony_ci stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 877bf215546Sopenharmony_ci 878bf215546Sopenharmony_ci // now find character position down a row 879bf215546Sopenharmony_ci if (find.length) { 880bf215546Sopenharmony_ci float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 881bf215546Sopenharmony_ci float x; 882bf215546Sopenharmony_ci int start = find.first_char + find.length; 883bf215546Sopenharmony_ci state->cursor = start; 884bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 885bf215546Sopenharmony_ci x = row.x0; 886bf215546Sopenharmony_ci for (i=0; i < row.num_chars; ++i) { 887bf215546Sopenharmony_ci float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); 888bf215546Sopenharmony_ci #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 889bf215546Sopenharmony_ci if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 890bf215546Sopenharmony_ci break; 891bf215546Sopenharmony_ci #endif 892bf215546Sopenharmony_ci x += dx; 893bf215546Sopenharmony_ci if (x > goal_x) 894bf215546Sopenharmony_ci break; 895bf215546Sopenharmony_ci ++state->cursor; 896bf215546Sopenharmony_ci } 897bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 898bf215546Sopenharmony_ci 899bf215546Sopenharmony_ci state->has_preferred_x = 1; 900bf215546Sopenharmony_ci state->preferred_x = goal_x; 901bf215546Sopenharmony_ci 902bf215546Sopenharmony_ci if (sel) 903bf215546Sopenharmony_ci state->select_end = state->cursor; 904bf215546Sopenharmony_ci } 905bf215546Sopenharmony_ci break; 906bf215546Sopenharmony_ci } 907bf215546Sopenharmony_ci 908bf215546Sopenharmony_ci case STB_TEXTEDIT_K_UP: 909bf215546Sopenharmony_ci case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { 910bf215546Sopenharmony_ci StbFindState find; 911bf215546Sopenharmony_ci StbTexteditRow row; 912bf215546Sopenharmony_ci int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; 913bf215546Sopenharmony_ci 914bf215546Sopenharmony_ci if (state->single_line) { 915bf215546Sopenharmony_ci // on windows, up&down become left&right 916bf215546Sopenharmony_ci key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); 917bf215546Sopenharmony_ci goto retry; 918bf215546Sopenharmony_ci } 919bf215546Sopenharmony_ci 920bf215546Sopenharmony_ci if (sel) 921bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 922bf215546Sopenharmony_ci else if (STB_TEXT_HAS_SELECTION(state)) 923bf215546Sopenharmony_ci stb_textedit_move_to_first(state); 924bf215546Sopenharmony_ci 925bf215546Sopenharmony_ci // compute current position of cursor point 926bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 927bf215546Sopenharmony_ci stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); 928bf215546Sopenharmony_ci 929bf215546Sopenharmony_ci // can only go up if there's a previous row 930bf215546Sopenharmony_ci if (find.prev_first != find.first_char) { 931bf215546Sopenharmony_ci // now find character position up a row 932bf215546Sopenharmony_ci float goal_x = state->has_preferred_x ? state->preferred_x : find.x; 933bf215546Sopenharmony_ci float x; 934bf215546Sopenharmony_ci state->cursor = find.prev_first; 935bf215546Sopenharmony_ci STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); 936bf215546Sopenharmony_ci x = row.x0; 937bf215546Sopenharmony_ci for (i=0; i < row.num_chars; ++i) { 938bf215546Sopenharmony_ci float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); 939bf215546Sopenharmony_ci #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE 940bf215546Sopenharmony_ci if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) 941bf215546Sopenharmony_ci break; 942bf215546Sopenharmony_ci #endif 943bf215546Sopenharmony_ci x += dx; 944bf215546Sopenharmony_ci if (x > goal_x) 945bf215546Sopenharmony_ci break; 946bf215546Sopenharmony_ci ++state->cursor; 947bf215546Sopenharmony_ci } 948bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 949bf215546Sopenharmony_ci 950bf215546Sopenharmony_ci state->has_preferred_x = 1; 951bf215546Sopenharmony_ci state->preferred_x = goal_x; 952bf215546Sopenharmony_ci 953bf215546Sopenharmony_ci if (sel) 954bf215546Sopenharmony_ci state->select_end = state->cursor; 955bf215546Sopenharmony_ci } 956bf215546Sopenharmony_ci break; 957bf215546Sopenharmony_ci } 958bf215546Sopenharmony_ci 959bf215546Sopenharmony_ci case STB_TEXTEDIT_K_DELETE: 960bf215546Sopenharmony_ci case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: 961bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 962bf215546Sopenharmony_ci stb_textedit_delete_selection(str, state); 963bf215546Sopenharmony_ci else { 964bf215546Sopenharmony_ci int n = STB_TEXTEDIT_STRINGLEN(str); 965bf215546Sopenharmony_ci if (state->cursor < n) 966bf215546Sopenharmony_ci stb_textedit_delete(str, state, state->cursor, 1); 967bf215546Sopenharmony_ci } 968bf215546Sopenharmony_ci state->has_preferred_x = 0; 969bf215546Sopenharmony_ci break; 970bf215546Sopenharmony_ci 971bf215546Sopenharmony_ci case STB_TEXTEDIT_K_BACKSPACE: 972bf215546Sopenharmony_ci case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: 973bf215546Sopenharmony_ci if (STB_TEXT_HAS_SELECTION(state)) 974bf215546Sopenharmony_ci stb_textedit_delete_selection(str, state); 975bf215546Sopenharmony_ci else { 976bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 977bf215546Sopenharmony_ci if (state->cursor > 0) { 978bf215546Sopenharmony_ci stb_textedit_delete(str, state, state->cursor-1, 1); 979bf215546Sopenharmony_ci --state->cursor; 980bf215546Sopenharmony_ci } 981bf215546Sopenharmony_ci } 982bf215546Sopenharmony_ci state->has_preferred_x = 0; 983bf215546Sopenharmony_ci break; 984bf215546Sopenharmony_ci 985bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_TEXTSTART2 986bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTSTART2: 987bf215546Sopenharmony_ci#endif 988bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTSTART: 989bf215546Sopenharmony_ci state->cursor = state->select_start = state->select_end = 0; 990bf215546Sopenharmony_ci state->has_preferred_x = 0; 991bf215546Sopenharmony_ci break; 992bf215546Sopenharmony_ci 993bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_TEXTEND2 994bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTEND2: 995bf215546Sopenharmony_ci#endif 996bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTEND: 997bf215546Sopenharmony_ci state->cursor = STB_TEXTEDIT_STRINGLEN(str); 998bf215546Sopenharmony_ci state->select_start = state->select_end = 0; 999bf215546Sopenharmony_ci state->has_preferred_x = 0; 1000bf215546Sopenharmony_ci break; 1001bf215546Sopenharmony_ci 1002bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_TEXTSTART2 1003bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: 1004bf215546Sopenharmony_ci#endif 1005bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: 1006bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 1007bf215546Sopenharmony_ci state->cursor = state->select_end = 0; 1008bf215546Sopenharmony_ci state->has_preferred_x = 0; 1009bf215546Sopenharmony_ci break; 1010bf215546Sopenharmony_ci 1011bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_TEXTEND2 1012bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: 1013bf215546Sopenharmony_ci#endif 1014bf215546Sopenharmony_ci case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: 1015bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 1016bf215546Sopenharmony_ci state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); 1017bf215546Sopenharmony_ci state->has_preferred_x = 0; 1018bf215546Sopenharmony_ci break; 1019bf215546Sopenharmony_ci 1020bf215546Sopenharmony_ci 1021bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_LINESTART2 1022bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINESTART2: 1023bf215546Sopenharmony_ci#endif 1024bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINESTART: 1025bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 1026bf215546Sopenharmony_ci stb_textedit_move_to_first(state); 1027bf215546Sopenharmony_ci if (state->single_line) 1028bf215546Sopenharmony_ci state->cursor = 0; 1029bf215546Sopenharmony_ci else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1030bf215546Sopenharmony_ci --state->cursor; 1031bf215546Sopenharmony_ci state->has_preferred_x = 0; 1032bf215546Sopenharmony_ci break; 1033bf215546Sopenharmony_ci 1034bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_LINEEND2 1035bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINEEND2: 1036bf215546Sopenharmony_ci#endif 1037bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINEEND: { 1038bf215546Sopenharmony_ci int n = STB_TEXTEDIT_STRINGLEN(str); 1039bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 1040bf215546Sopenharmony_ci stb_textedit_move_to_first(state); 1041bf215546Sopenharmony_ci if (state->single_line) 1042bf215546Sopenharmony_ci state->cursor = n; 1043bf215546Sopenharmony_ci else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1044bf215546Sopenharmony_ci ++state->cursor; 1045bf215546Sopenharmony_ci state->has_preferred_x = 0; 1046bf215546Sopenharmony_ci break; 1047bf215546Sopenharmony_ci } 1048bf215546Sopenharmony_ci 1049bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_LINESTART2 1050bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: 1051bf215546Sopenharmony_ci#endif 1052bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: 1053bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 1054bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 1055bf215546Sopenharmony_ci if (state->single_line) 1056bf215546Sopenharmony_ci state->cursor = 0; 1057bf215546Sopenharmony_ci else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) 1058bf215546Sopenharmony_ci --state->cursor; 1059bf215546Sopenharmony_ci state->select_end = state->cursor; 1060bf215546Sopenharmony_ci state->has_preferred_x = 0; 1061bf215546Sopenharmony_ci break; 1062bf215546Sopenharmony_ci 1063bf215546Sopenharmony_ci#ifdef STB_TEXTEDIT_K_LINEEND2 1064bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: 1065bf215546Sopenharmony_ci#endif 1066bf215546Sopenharmony_ci case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { 1067bf215546Sopenharmony_ci int n = STB_TEXTEDIT_STRINGLEN(str); 1068bf215546Sopenharmony_ci stb_textedit_clamp(str, state); 1069bf215546Sopenharmony_ci stb_textedit_prep_selection_at_cursor(state); 1070bf215546Sopenharmony_ci if (state->single_line) 1071bf215546Sopenharmony_ci state->cursor = n; 1072bf215546Sopenharmony_ci else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) 1073bf215546Sopenharmony_ci ++state->cursor; 1074bf215546Sopenharmony_ci state->select_end = state->cursor; 1075bf215546Sopenharmony_ci state->has_preferred_x = 0; 1076bf215546Sopenharmony_ci break; 1077bf215546Sopenharmony_ci } 1078bf215546Sopenharmony_ci 1079bf215546Sopenharmony_ci// @TODO: 1080bf215546Sopenharmony_ci// STB_TEXTEDIT_K_PGUP - move cursor up a page 1081bf215546Sopenharmony_ci// STB_TEXTEDIT_K_PGDOWN - move cursor down a page 1082bf215546Sopenharmony_ci } 1083bf215546Sopenharmony_ci} 1084bf215546Sopenharmony_ci 1085bf215546Sopenharmony_ci///////////////////////////////////////////////////////////////////////////// 1086bf215546Sopenharmony_ci// 1087bf215546Sopenharmony_ci// Undo processing 1088bf215546Sopenharmony_ci// 1089bf215546Sopenharmony_ci// @OPTIMIZE: the undo/redo buffer should be circular 1090bf215546Sopenharmony_ci 1091bf215546Sopenharmony_cistatic void stb_textedit_flush_redo(StbUndoState *state) 1092bf215546Sopenharmony_ci{ 1093bf215546Sopenharmony_ci state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1094bf215546Sopenharmony_ci state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1095bf215546Sopenharmony_ci} 1096bf215546Sopenharmony_ci 1097bf215546Sopenharmony_ci// discard the oldest entry in the undo list 1098bf215546Sopenharmony_cistatic void stb_textedit_discard_undo(StbUndoState *state) 1099bf215546Sopenharmony_ci{ 1100bf215546Sopenharmony_ci if (state->undo_point > 0) { 1101bf215546Sopenharmony_ci // if the 0th undo state has characters, clean those up 1102bf215546Sopenharmony_ci if (state->undo_rec[0].char_storage >= 0) { 1103bf215546Sopenharmony_ci int n = state->undo_rec[0].insert_length, i; 1104bf215546Sopenharmony_ci // delete n characters from all other records 1105bf215546Sopenharmony_ci state->undo_char_point -= n; 1106bf215546Sopenharmony_ci STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); 1107bf215546Sopenharmony_ci for (i=0; i < state->undo_point; ++i) 1108bf215546Sopenharmony_ci if (state->undo_rec[i].char_storage >= 0) 1109bf215546Sopenharmony_ci state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it 1110bf215546Sopenharmony_ci } 1111bf215546Sopenharmony_ci --state->undo_point; 1112bf215546Sopenharmony_ci STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); 1113bf215546Sopenharmony_ci } 1114bf215546Sopenharmony_ci} 1115bf215546Sopenharmony_ci 1116bf215546Sopenharmony_ci// discard the oldest entry in the redo list--it's bad if this 1117bf215546Sopenharmony_ci// ever happens, but because undo & redo have to store the actual 1118bf215546Sopenharmony_ci// characters in different cases, the redo character buffer can 1119bf215546Sopenharmony_ci// fill up even though the undo buffer didn't 1120bf215546Sopenharmony_cistatic void stb_textedit_discard_redo(StbUndoState *state) 1121bf215546Sopenharmony_ci{ 1122bf215546Sopenharmony_ci int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; 1123bf215546Sopenharmony_ci 1124bf215546Sopenharmony_ci if (state->redo_point <= k) { 1125bf215546Sopenharmony_ci // if the k'th undo state has characters, clean those up 1126bf215546Sopenharmony_ci if (state->undo_rec[k].char_storage >= 0) { 1127bf215546Sopenharmony_ci int n = state->undo_rec[k].insert_length, i; 1128bf215546Sopenharmony_ci // move the remaining redo character data to the end of the buffer 1129bf215546Sopenharmony_ci state->redo_char_point += n; 1130bf215546Sopenharmony_ci 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))); 1131bf215546Sopenharmony_ci // adjust the position of all the other records to account for above memmove 1132bf215546Sopenharmony_ci for (i=state->redo_point; i < k; ++i) 1133bf215546Sopenharmony_ci if (state->undo_rec[i].char_storage >= 0) 1134bf215546Sopenharmony_ci state->undo_rec[i].char_storage += n; 1135bf215546Sopenharmony_ci } 1136bf215546Sopenharmony_ci // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' 1137bf215546Sopenharmony_ci // {DEAR IMGUI] 1138bf215546Sopenharmony_ci size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); 1139bf215546Sopenharmony_ci const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; 1140bf215546Sopenharmony_ci const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; 1141bf215546Sopenharmony_ci IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); 1142bf215546Sopenharmony_ci IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); 1143bf215546Sopenharmony_ci STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); 1144bf215546Sopenharmony_ci 1145bf215546Sopenharmony_ci // now move redo_point to point to the new one 1146bf215546Sopenharmony_ci ++state->redo_point; 1147bf215546Sopenharmony_ci } 1148bf215546Sopenharmony_ci} 1149bf215546Sopenharmony_ci 1150bf215546Sopenharmony_cistatic StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) 1151bf215546Sopenharmony_ci{ 1152bf215546Sopenharmony_ci // any time we create a new undo record, we discard redo 1153bf215546Sopenharmony_ci stb_textedit_flush_redo(state); 1154bf215546Sopenharmony_ci 1155bf215546Sopenharmony_ci // if we have no free records, we have to make room, by sliding the 1156bf215546Sopenharmony_ci // existing records down 1157bf215546Sopenharmony_ci if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1158bf215546Sopenharmony_ci stb_textedit_discard_undo(state); 1159bf215546Sopenharmony_ci 1160bf215546Sopenharmony_ci // if the characters to store won't possibly fit in the buffer, we can't undo 1161bf215546Sopenharmony_ci if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { 1162bf215546Sopenharmony_ci state->undo_point = 0; 1163bf215546Sopenharmony_ci state->undo_char_point = 0; 1164bf215546Sopenharmony_ci return NULL; 1165bf215546Sopenharmony_ci } 1166bf215546Sopenharmony_ci 1167bf215546Sopenharmony_ci // if we don't have enough free characters in the buffer, we have to make room 1168bf215546Sopenharmony_ci while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) 1169bf215546Sopenharmony_ci stb_textedit_discard_undo(state); 1170bf215546Sopenharmony_ci 1171bf215546Sopenharmony_ci return &state->undo_rec[state->undo_point++]; 1172bf215546Sopenharmony_ci} 1173bf215546Sopenharmony_ci 1174bf215546Sopenharmony_cistatic STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) 1175bf215546Sopenharmony_ci{ 1176bf215546Sopenharmony_ci StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); 1177bf215546Sopenharmony_ci if (r == NULL) 1178bf215546Sopenharmony_ci return NULL; 1179bf215546Sopenharmony_ci 1180bf215546Sopenharmony_ci r->where = pos; 1181bf215546Sopenharmony_ci r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len; 1182bf215546Sopenharmony_ci r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len; 1183bf215546Sopenharmony_ci 1184bf215546Sopenharmony_ci if (insert_len == 0) { 1185bf215546Sopenharmony_ci r->char_storage = -1; 1186bf215546Sopenharmony_ci return NULL; 1187bf215546Sopenharmony_ci } else { 1188bf215546Sopenharmony_ci r->char_storage = state->undo_char_point; 1189bf215546Sopenharmony_ci state->undo_char_point += insert_len; 1190bf215546Sopenharmony_ci return &state->undo_char[r->char_storage]; 1191bf215546Sopenharmony_ci } 1192bf215546Sopenharmony_ci} 1193bf215546Sopenharmony_ci 1194bf215546Sopenharmony_cistatic void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1195bf215546Sopenharmony_ci{ 1196bf215546Sopenharmony_ci StbUndoState *s = &state->undostate; 1197bf215546Sopenharmony_ci StbUndoRecord u, *r; 1198bf215546Sopenharmony_ci if (s->undo_point == 0) 1199bf215546Sopenharmony_ci return; 1200bf215546Sopenharmony_ci 1201bf215546Sopenharmony_ci // we need to do two things: apply the undo record, and create a redo record 1202bf215546Sopenharmony_ci u = s->undo_rec[s->undo_point-1]; 1203bf215546Sopenharmony_ci r = &s->undo_rec[s->redo_point-1]; 1204bf215546Sopenharmony_ci r->char_storage = -1; 1205bf215546Sopenharmony_ci 1206bf215546Sopenharmony_ci r->insert_length = u.delete_length; 1207bf215546Sopenharmony_ci r->delete_length = u.insert_length; 1208bf215546Sopenharmony_ci r->where = u.where; 1209bf215546Sopenharmony_ci 1210bf215546Sopenharmony_ci if (u.delete_length) { 1211bf215546Sopenharmony_ci // if the undo record says to delete characters, then the redo record will 1212bf215546Sopenharmony_ci // need to re-insert the characters that get deleted, so we need to store 1213bf215546Sopenharmony_ci // them. 1214bf215546Sopenharmony_ci 1215bf215546Sopenharmony_ci // there are three cases: 1216bf215546Sopenharmony_ci // there's enough room to store the characters 1217bf215546Sopenharmony_ci // characters stored for *redoing* don't leave room for redo 1218bf215546Sopenharmony_ci // characters stored for *undoing* don't leave room for redo 1219bf215546Sopenharmony_ci // if the last is true, we have to bail 1220bf215546Sopenharmony_ci 1221bf215546Sopenharmony_ci if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { 1222bf215546Sopenharmony_ci // the undo records take up too much character space; there's no space to store the redo characters 1223bf215546Sopenharmony_ci r->insert_length = 0; 1224bf215546Sopenharmony_ci } else { 1225bf215546Sopenharmony_ci int i; 1226bf215546Sopenharmony_ci 1227bf215546Sopenharmony_ci // there's definitely room to store the characters eventually 1228bf215546Sopenharmony_ci while (s->undo_char_point + u.delete_length > s->redo_char_point) { 1229bf215546Sopenharmony_ci // should never happen: 1230bf215546Sopenharmony_ci if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1231bf215546Sopenharmony_ci return; 1232bf215546Sopenharmony_ci // there's currently not enough room, so discard a redo record 1233bf215546Sopenharmony_ci stb_textedit_discard_redo(s); 1234bf215546Sopenharmony_ci } 1235bf215546Sopenharmony_ci r = &s->undo_rec[s->redo_point-1]; 1236bf215546Sopenharmony_ci 1237bf215546Sopenharmony_ci r->char_storage = s->redo_char_point - u.delete_length; 1238bf215546Sopenharmony_ci s->redo_char_point = s->redo_char_point - u.delete_length; 1239bf215546Sopenharmony_ci 1240bf215546Sopenharmony_ci // now save the characters 1241bf215546Sopenharmony_ci for (i=0; i < u.delete_length; ++i) 1242bf215546Sopenharmony_ci s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); 1243bf215546Sopenharmony_ci } 1244bf215546Sopenharmony_ci 1245bf215546Sopenharmony_ci // now we can carry out the deletion 1246bf215546Sopenharmony_ci STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); 1247bf215546Sopenharmony_ci } 1248bf215546Sopenharmony_ci 1249bf215546Sopenharmony_ci // check type of recorded action: 1250bf215546Sopenharmony_ci if (u.insert_length) { 1251bf215546Sopenharmony_ci // easy case: was a deletion, so we need to insert n characters 1252bf215546Sopenharmony_ci STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); 1253bf215546Sopenharmony_ci s->undo_char_point -= u.insert_length; 1254bf215546Sopenharmony_ci } 1255bf215546Sopenharmony_ci 1256bf215546Sopenharmony_ci state->cursor = u.where + u.insert_length; 1257bf215546Sopenharmony_ci 1258bf215546Sopenharmony_ci s->undo_point--; 1259bf215546Sopenharmony_ci s->redo_point--; 1260bf215546Sopenharmony_ci} 1261bf215546Sopenharmony_ci 1262bf215546Sopenharmony_cistatic void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) 1263bf215546Sopenharmony_ci{ 1264bf215546Sopenharmony_ci StbUndoState *s = &state->undostate; 1265bf215546Sopenharmony_ci StbUndoRecord *u, r; 1266bf215546Sopenharmony_ci if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) 1267bf215546Sopenharmony_ci return; 1268bf215546Sopenharmony_ci 1269bf215546Sopenharmony_ci // we need to do two things: apply the redo record, and create an undo record 1270bf215546Sopenharmony_ci u = &s->undo_rec[s->undo_point]; 1271bf215546Sopenharmony_ci r = s->undo_rec[s->redo_point]; 1272bf215546Sopenharmony_ci 1273bf215546Sopenharmony_ci // we KNOW there must be room for the undo record, because the redo record 1274bf215546Sopenharmony_ci // was derived from an undo record 1275bf215546Sopenharmony_ci 1276bf215546Sopenharmony_ci u->delete_length = r.insert_length; 1277bf215546Sopenharmony_ci u->insert_length = r.delete_length; 1278bf215546Sopenharmony_ci u->where = r.where; 1279bf215546Sopenharmony_ci u->char_storage = -1; 1280bf215546Sopenharmony_ci 1281bf215546Sopenharmony_ci if (r.delete_length) { 1282bf215546Sopenharmony_ci // the redo record requires us to delete characters, so the undo record 1283bf215546Sopenharmony_ci // needs to store the characters 1284bf215546Sopenharmony_ci 1285bf215546Sopenharmony_ci if (s->undo_char_point + u->insert_length > s->redo_char_point) { 1286bf215546Sopenharmony_ci u->insert_length = 0; 1287bf215546Sopenharmony_ci u->delete_length = 0; 1288bf215546Sopenharmony_ci } else { 1289bf215546Sopenharmony_ci int i; 1290bf215546Sopenharmony_ci u->char_storage = s->undo_char_point; 1291bf215546Sopenharmony_ci s->undo_char_point = s->undo_char_point + u->insert_length; 1292bf215546Sopenharmony_ci 1293bf215546Sopenharmony_ci // now save the characters 1294bf215546Sopenharmony_ci for (i=0; i < u->insert_length; ++i) 1295bf215546Sopenharmony_ci s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); 1296bf215546Sopenharmony_ci } 1297bf215546Sopenharmony_ci 1298bf215546Sopenharmony_ci STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); 1299bf215546Sopenharmony_ci } 1300bf215546Sopenharmony_ci 1301bf215546Sopenharmony_ci if (r.insert_length) { 1302bf215546Sopenharmony_ci // easy case: need to insert n characters 1303bf215546Sopenharmony_ci STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); 1304bf215546Sopenharmony_ci s->redo_char_point += r.insert_length; 1305bf215546Sopenharmony_ci } 1306bf215546Sopenharmony_ci 1307bf215546Sopenharmony_ci state->cursor = r.where + r.insert_length; 1308bf215546Sopenharmony_ci 1309bf215546Sopenharmony_ci s->undo_point++; 1310bf215546Sopenharmony_ci s->redo_point++; 1311bf215546Sopenharmony_ci} 1312bf215546Sopenharmony_ci 1313bf215546Sopenharmony_cistatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) 1314bf215546Sopenharmony_ci{ 1315bf215546Sopenharmony_ci stb_text_createundo(&state->undostate, where, 0, length); 1316bf215546Sopenharmony_ci} 1317bf215546Sopenharmony_ci 1318bf215546Sopenharmony_cistatic void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) 1319bf215546Sopenharmony_ci{ 1320bf215546Sopenharmony_ci int i; 1321bf215546Sopenharmony_ci STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); 1322bf215546Sopenharmony_ci if (p) { 1323bf215546Sopenharmony_ci for (i=0; i < length; ++i) 1324bf215546Sopenharmony_ci p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1325bf215546Sopenharmony_ci } 1326bf215546Sopenharmony_ci} 1327bf215546Sopenharmony_ci 1328bf215546Sopenharmony_cistatic void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) 1329bf215546Sopenharmony_ci{ 1330bf215546Sopenharmony_ci int i; 1331bf215546Sopenharmony_ci STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); 1332bf215546Sopenharmony_ci if (p) { 1333bf215546Sopenharmony_ci for (i=0; i < old_length; ++i) 1334bf215546Sopenharmony_ci p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); 1335bf215546Sopenharmony_ci } 1336bf215546Sopenharmony_ci} 1337bf215546Sopenharmony_ci 1338bf215546Sopenharmony_ci// reset the state to default 1339bf215546Sopenharmony_cistatic void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) 1340bf215546Sopenharmony_ci{ 1341bf215546Sopenharmony_ci state->undostate.undo_point = 0; 1342bf215546Sopenharmony_ci state->undostate.undo_char_point = 0; 1343bf215546Sopenharmony_ci state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; 1344bf215546Sopenharmony_ci state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; 1345bf215546Sopenharmony_ci state->select_end = state->select_start = 0; 1346bf215546Sopenharmony_ci state->cursor = 0; 1347bf215546Sopenharmony_ci state->has_preferred_x = 0; 1348bf215546Sopenharmony_ci state->preferred_x = 0; 1349bf215546Sopenharmony_ci state->cursor_at_end_of_line = 0; 1350bf215546Sopenharmony_ci state->initialized = 1; 1351bf215546Sopenharmony_ci state->single_line = (unsigned char) is_single_line; 1352bf215546Sopenharmony_ci state->insert_mode = 0; 1353bf215546Sopenharmony_ci} 1354bf215546Sopenharmony_ci 1355bf215546Sopenharmony_ci// API initialize 1356bf215546Sopenharmony_cistatic void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) 1357bf215546Sopenharmony_ci{ 1358bf215546Sopenharmony_ci stb_textedit_clear_state(state, is_single_line); 1359bf215546Sopenharmony_ci} 1360bf215546Sopenharmony_ci 1361bf215546Sopenharmony_ci#if defined(__GNUC__) || defined(__clang__) 1362bf215546Sopenharmony_ci#pragma GCC diagnostic push 1363bf215546Sopenharmony_ci#pragma GCC diagnostic ignored "-Wcast-qual" 1364bf215546Sopenharmony_ci#endif 1365bf215546Sopenharmony_ci 1366bf215546Sopenharmony_cistatic int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) 1367bf215546Sopenharmony_ci{ 1368bf215546Sopenharmony_ci return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len); 1369bf215546Sopenharmony_ci} 1370bf215546Sopenharmony_ci 1371bf215546Sopenharmony_ci#if defined(__GNUC__) || defined(__clang__) 1372bf215546Sopenharmony_ci#pragma GCC diagnostic pop 1373bf215546Sopenharmony_ci#endif 1374bf215546Sopenharmony_ci 1375bf215546Sopenharmony_ci#endif//STB_TEXTEDIT_IMPLEMENTATION 1376bf215546Sopenharmony_ci 1377bf215546Sopenharmony_ci/* 1378bf215546Sopenharmony_ci------------------------------------------------------------------------------ 1379bf215546Sopenharmony_ciThis software is available under 2 licenses -- choose whichever you prefer. 1380bf215546Sopenharmony_ci------------------------------------------------------------------------------ 1381bf215546Sopenharmony_ciALTERNATIVE A - MIT License 1382bf215546Sopenharmony_ciCopyright (c) 2017 Sean Barrett 1383bf215546Sopenharmony_ciPermission is hereby granted, free of charge, to any person obtaining a copy of 1384bf215546Sopenharmony_cithis software and associated documentation files (the "Software"), to deal in 1385bf215546Sopenharmony_cithe Software without restriction, including without limitation the rights to 1386bf215546Sopenharmony_ciuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1387bf215546Sopenharmony_ciof the Software, and to permit persons to whom the Software is furnished to do 1388bf215546Sopenharmony_ciso, subject to the following conditions: 1389bf215546Sopenharmony_ciThe above copyright notice and this permission notice shall be included in all 1390bf215546Sopenharmony_cicopies or substantial portions of the Software. 1391bf215546Sopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1392bf215546Sopenharmony_ciIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1393bf215546Sopenharmony_ciFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1394bf215546Sopenharmony_ciAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1395bf215546Sopenharmony_ciLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1396bf215546Sopenharmony_ciOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1397bf215546Sopenharmony_ciSOFTWARE. 1398bf215546Sopenharmony_ci------------------------------------------------------------------------------ 1399bf215546Sopenharmony_ciALTERNATIVE B - Public Domain (www.unlicense.org) 1400bf215546Sopenharmony_ciThis is free and unencumbered software released into the public domain. 1401bf215546Sopenharmony_ciAnyone is free to copy, modify, publish, use, compile, sell, or distribute this 1402bf215546Sopenharmony_cisoftware, either in source code form or as a compiled binary, for any purpose, 1403bf215546Sopenharmony_cicommercial or non-commercial, and by any means. 1404bf215546Sopenharmony_ciIn jurisdictions that recognize copyright laws, the author or authors of this 1405bf215546Sopenharmony_cisoftware dedicate any and all copyright interest in the software to the public 1406bf215546Sopenharmony_cidomain. We make this dedication for the benefit of the public at large and to 1407bf215546Sopenharmony_cithe detriment of our heirs and successors. We intend this dedication to be an 1408bf215546Sopenharmony_ciovert act of relinquishment in perpetuity of all present and future rights to 1409bf215546Sopenharmony_cithis software under copyright law. 1410bf215546Sopenharmony_ciTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1411bf215546Sopenharmony_ciIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1412bf215546Sopenharmony_ciFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1413bf215546Sopenharmony_ciAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1414bf215546Sopenharmony_ciACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1415bf215546Sopenharmony_ciWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1416bf215546Sopenharmony_ci------------------------------------------------------------------------------ 1417bf215546Sopenharmony_ci*/ 1418