Vuo  2.0.0
VuoTextEdit.h
Go to the documentation of this file.
1 
10 #pragma once
11 
12 #include "VuoText.h"
13 
17 #ifndef STB_TEXTEDIT_CHARTYPE
18 #define STB_TEXTEDIT_CHARTYPE uint64_t
19 #endif
20 
21 #include <stdlib.h>
22 #include "VuoHeap.h"
23 #include "VuoImageText.h"
24 #include "stb_textedit.h"
25 #include "VuoClipboard.h"
26 #include "VuoModifierKey.h"
27 
28 #define COMMAND_HI ((((uint64_t)VuoModifierKey_Command) << 32))
29 #define CONTROL_HI ((((uint64_t)VuoModifierKey_Control) << 32))
30 #define OPTION_HI ((((uint64_t)VuoModifierKey_Option) << 32))
31 #define SHIFT_HI ((((uint64_t)VuoModifierKey_Shift) << 32))
32 
33 #define KEYCODE_COPY 0x3
34 #define KEYCODE_PASTE 0x16
35 #define KEYCODE_CUT 0x18
36 #define KEYCODE_ESC 0x1b
37 #define KEYCODE_TAB 0x9
38 #define KEYCODE_RETURN 0xD
39 
40 static char STB_TEXTEDIT_NEWLINE = '\n';
42 
43 #define STB_TEXTEDIT_K_LEFT 0x0001C
44 #define STB_TEXTEDIT_K_RIGHT 0x0001D
45 #define STB_TEXTEDIT_K_UP 0x0001E
46 #define STB_TEXTEDIT_K_DOWN 0x0001F
47 #define STB_TEXTEDIT_K_LINESTART (COMMAND_HI | STB_TEXTEDIT_K_LEFT)
48 #define STB_TEXTEDIT_K_LINEEND (COMMAND_HI | STB_TEXTEDIT_K_RIGHT)
49 #define STB_TEXTEDIT_K_TEXTSTART (COMMAND_HI | STB_TEXTEDIT_K_UP)
50 #define STB_TEXTEDIT_K_TEXTSTART2 (CONTROL_HI | 0x41)
51 #define STB_TEXTEDIT_K_TEXTEND (COMMAND_HI | STB_TEXTEDIT_K_DOWN)
52 #define STB_TEXTEDIT_K_TEXTEND2 (CONTROL_HI | 0x45)
53 #define STB_TEXTEDIT_K_DELETE 0x7f
54 #define STB_TEXTEDIT_K_BACKSPACE 0x00008
55 #define STB_TEXTEDIT_K_UNDO (COMMAND_HI | 0x1A)
56 #define STB_TEXTEDIT_K_REDO (COMMAND_HI | 0x19)
57 #define STB_TEXTEDIT_K_REDO2 ((COMMAND_HI | SHIFT_HI) | 0x1A)
58 #define STB_TEXTEDIT_K_WORDLEFT (OPTION_HI | STB_TEXTEDIT_K_LEFT)
59 #define STB_TEXTEDIT_K_WORDRIGHT (OPTION_HI | STB_TEXTEDIT_K_RIGHT)
60 #define STB_TEXTEDIT_K_DELETEWORD (OPTION_HI | STB_TEXTEDIT_K_BACKSPACE)
61 #define STB_TEXTEDIT_K_DELETEWORDRIGHT (OPTION_HI | STB_TEXTEDIT_K_DELETE)
62 #define STB_TEXTEDIT_K_DELETELINE (COMMAND_HI | STB_TEXTEDIT_K_BACKSPACE)
63 #define STB_TEXTEDIT_K_DELETELINERIGHT (COMMAND_HI | STB_TEXTEDIT_K_DELETE)
64 #define STB_TEXTEDIT_K_SHIFT SHIFT_HI
65 #define STB_TEXTEDIT_K_SELECTALL (COMMAND_HI | 0x1)
66 
67 #define STB_TEXTEDIT_GETWIDTH_NEWLINE 0
69 
74 
79 {
80  return ((uint64_t)utf32) | (((uint64_t)modifiers) << 32);
81 }
82 
88 {
89  uint32_t* utf32 = VuoText_getUtf32Values(text, length);
90  STB_TEXTEDIT_CHARTYPE* stbchars = (STB_TEXTEDIT_CHARTYPE*) malloc(sizeof(STB_TEXTEDIT_CHARTYPE) * (*length));
91  for(int i = 0; i < (*length); i++)
92  stbchars[i] = utf32[i];
93  free(utf32);
94  return stbchars;
95 }
96 
101 {
102  return VuoText_length(*obj);
103 }
104 
108 static bool VuoTextEdit_isNewLine(uint32_t utf32_char)
109 {
110  return utf32_char >= 0xA && utf32_char <= 0xD;
111 }
112 
116 static bool VuoTextEdit_isWhiteSpace(uint32_t utf32_char)
117 {
118  // control code or one of those weird white space chars between 0x2000-A
119  if( utf32_char < 33 || (utf32_char >= 0x2000 && utf32_char <= 0x200A) )
120  return true;
121 
122  return false;
123 }
124 
129 static bool VuoTextEdit_isSeparator(uint32_t utf32_char)
130 {
131  // control code or one of those weird white space chars between 0x2000-A
132  if( VuoTextEdit_isWhiteSpace(utf32_char) )
133  return true;
134 
135  size_t length = 0;
136 
137  uint32_t* separators = VuoText_getUtf32Values("./\\()\"'-:,;<>~!@#$%%^&*|+=[]{}`~?", &length);
138 
139  for(size_t i = 0; i < length; i++)
140  {
141  if(utf32_char == separators[i])
142  {
143  free(separators);
144  return true;
145  }
146  }
147 
148  free(separators);
149 
150  return false;
151 }
152 
156 static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state);
157 
162 static void VuoTextEdit_selectFromCursorToSeparator(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, bool (*separatorFunc)(uint32_t))
163 {
164  if(VuoText_isEmpty(*str))
165  return;
166 
168 
169  size_t len = 0;
170  uint32_t* chars = VuoText_getUtf32Values(*str, &len);
171  // select_start can be greater if placeholder text is present
172  int start = MIN(len, state->select_start);
173  state->select_start = 0;
174 
175  for(int i = start; i > 0; i--)
176  {
177  if( (*separatorFunc)(chars[i]) )
178  {
179  state->select_start = i + 1;
180  break;
181  }
182  }
183 
184  int prev_end = state->select_end;
185  state->select_end = len;
186  state->cursor = len;
187 
188  for(int i = prev_end; i < len; i++)
189  {
190  if( (*separatorFunc)(chars[i]) )
191  {
192  state->select_end = i;
193  state->cursor = i;
194  break;
195  }
196  }
197 }
198 
202 static void VuoTextEdit_copy(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
203 {
204  int start = MIN(state->select_start, state->select_end);
205  int end = MAX(state->select_start, state->select_end);
206  VuoText selection = VuoText_substring(*str, start + 1, end - start);
207  VuoLocal(selection);
208  VuoClipboard_setText(selection);
209 }
210 
214 static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);
215 
219 static int VuoTextEdit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
220 {
221  if( state->select_start != state->select_end )
222  {
223  VuoTextEdit_copy(str, state);
224  stb_textedit_cut(str, state);
225  return 1;
226  }
227  return 0;
228 }
229 
233 static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len);
234 
239 {
240  if(idx < 0)
241  return -1;
242 
243  // @todo It's inefficient to keep allocating a full utf32 array for a single char
244  size_t length;
245 
246  uint32_t* utf32 = VuoText_getUtf32Values(*obj, &length);
247 
248  if(idx >= length)
249  return -1;
250 
251  return (STB_TEXTEDIT_CHARTYPE) utf32[idx];
252 }
253 
259 {
260  int count = 1;
261 
262  for(int ii = 0; ii < STB_TEXTEDIT_STRINGLEN(str); ii++)
264  count++;
265 
266  return count;
267 }
268 
273 {
274  VuoList_VuoInteger newLines = VuoText_findOccurrences(text, "\n");
275  VuoLocal(newLines);
276 
277  VuoText res;
278 
279  if(VuoListGetCount_VuoInteger(newLines) >= maxLines)
280  {
281  VuoInteger trunc = VuoListGetValue_VuoInteger(newLines, maxLines);
282  // Subtract 1 because we don't want the \n
283  res = VuoText_substring(text, 1, trunc - 1);
284  }
285  else
286  {
287  res = VuoText_make(text);
288  }
289 
290  return res;
291 }
292 
296 static void VuoTextEdit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
297 {
298  VuoText clipboard = VuoClipboard_getContents();
299  VuoLocal(clipboard);
300 
301  int lineCount = stb_textedit_linecount(str);
302  // +1 because line_count is a count of \n, not lines.
303  VuoText res = VuoTextEdit_truncateLines(clipboard, (state->line_count - lineCount) + 1);
304  VuoLocal(res);
305 
306  size_t len = 0;
307  uint64_t* clip64 = VuoTextEdit_makeCharArrayWithVuoText(res, &len);
308  stb_textedit_paste(str, state, clip64, len);
309  free(clip64);
310 }
311 
315 static int stb_text_locate_coord(STB_TexteditState *state, float x, float y)
316 {
317  return VuoImageTextData_getNearestCharToPoint((VuoImageTextData) state->textImageData, VuoPoint2d_make(x, y));
318 }
319 
324 static void stb_textedit_find_charpos(StbFindState* find, STB_TEXTEDIT_STRING *str, int n, STB_TexteditState* state)
325 {
326  VuoImageTextData data = (VuoImageTextData)state->textImageData;
327 
328  unsigned int lineIndex = 0;
329  VuoPoint2d position = VuoImageTextData_getPositionForCharIndex(data, n, &lineIndex);
330 
331  find->x = position.x;
332  find->y = position.y;
333  find->height = data->lineHeight;
334  find->first_char = VuoImageTextData_getCharIndexForLine(data, lineIndex);
335  find->length = data->lineCounts[lineIndex];
336  find->prev_first = VuoImageTextData_getCharIndexForLine(data, MAX(0, ((int)lineIndex) - 1));
337 }
338 
342 static int VuoTextEdit_findLineIndex(int charIndex, STB_TexteditState* state)
343 {
344  VuoImageTextData data = (VuoImageTextData) state->textImageData;
345  unsigned int lineIndex = 0;
346  VuoImageTextData_getPositionForCharIndex(data, charIndex, &lineIndex);
347  return VuoImageTextData_getCharIndexForLine(data, lineIndex);
348 }
349 
354 {
355  // fprintf(stderr, "raw: %llu (%llu) hi: %llu lo: %u\n", key, (OPTION_HI | 0xd), key & VuoModifierKey_Any, (uint32_t) ((key & ~VuoModifierKey_Any) & 0xFFFF));
356 
357  // special case - alt+enter inserts new line, alt+tab inserts tab
358  if(key == (OPTION_HI | KEYCODE_RETURN))
359  return STB_TEXTEDIT_NEWLINE;
360  else if(key == (OPTION_HI | KEYCODE_TAB))
361  return '\t';
362 
363  uint32_t lo = (uint32_t) (key & 0xFFFF);
364 
365  return lo > 31 && lo < UINT32_MAX ? lo : 0;
366 }
367 
369 #define STB_TEXTEDIT_IS_SPACE(x) VuoTextEdit_isSeparator(((uint32_t)x))
370 
372 #define STB_TEXTEDIT_IS_SEPARATOR(x) VuoTextEdit_isSeparator(((uint32_t)x))
373 
381 static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int n, int i, STB_TexteditState* state)
382 {
383  VuoImageTextData data = (VuoImageTextData) state->textImageData;
384  return data->charAdvance[n + i];
385 }
386 
390 static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx, STB_TexteditState* state)
391 {
392  unsigned int len = 1;
393  VuoRectangle rect = VuoImageTextData_layoutRowAtIndex((VuoImageTextData) state->textImageData, line_start_idx, &len);
394  r->x0 = rect.center.x - (rect.size.x * .5);
395  r->x1 = rect.size.x;
396  r->baseline_y_delta = rect.size.y;
397  r->ymin = rect.center.y - (rect.size.y * .5f);
398  r->ymax = rect.size.y;
399  r->num_chars = len;
400 }
401 
405 static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
406 {
407  VuoText old = *obj;
408  *obj = VuoText_removeAt(old, pos + 1, n);
409  VuoRetain(*obj);
410  VuoRelease(old);
411 }
412 
416 static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const STB_TEXTEDIT_CHARTYPE* new_text, int new_text_len)
417 {
418  uint32_t new_text_32[new_text_len];
419 
420  for(int i = 0; i < new_text_len; i++)
421  new_text_32[i] = (uint32_t) (new_text[i] & 0xFFFF);
422 
423  VuoText old = *obj;
424  VuoText ascii = VuoText_makeFromUtf32(new_text_32, new_text_len);
425  VuoLocal(ascii);
426  *obj = VuoText_insert(old, pos + 1, ascii);
427  VuoRelease(old);
428  VuoRetain(*obj);
429  return true;
430 }