Vuo  2.3.2
VuoTextFieldInternal.cc
Go to the documentation of this file.
1 
10 #include "VuoTextFieldInternal.h"
11 #include "module.h"
12 
13 extern "C"
14 {
15 #ifdef VUO_COMPILER
17  "title" : "VuoTextFieldInternal",
18  "dependencies" : [ "VuoKeyboard", "VuoClipboard" ],
19  });
20 #endif
21 }
22 
23 #include "VuoUiThemeBase.hh"
24 
28 static VuoText CreateVuoText(const char* string)
29 {
30  VuoText t = VuoText_make(string);
31  VuoRetain(t);
32  return t;
33 }
34 
39 {
40  text = NULL;
41  placeholder = CreateVuoText("Placeholder Text");
42  textImageData = NULL;
43  isHovering = false;
44  isFocused = false;
45  stb_textedit_initialize_state(&textEditState, lines);
46 }
47 
52 {
53  if(text != NULL)
55 
56  if(placeholder != NULL)
58 
60 }
61 
66 {
67  this->context = context;
68  state = new VuoTextFieldState(numLines);
69  lineCount = numLines;
71  screenSize = VuoPoint2d_make(512, 512);
72  screenBackingScaleFactor = 1;
73  position = VuoPoint2d_make(0,0);
74  anchor = VuoAnchor_makeCentered();
75  theme = NULL;
76  validateCharInput = NULL;
77  validateTextInput = NULL;
78 }
79 
84 {
85  VuoRelease(theme);
86  delete state;
87 }
88 
93 {
94  state->textEditState.line_count = lines;
95  lineCount = lines;
96  SetText(state->text);
97 }
98 
102 void VuoTextFieldInternal::SetPosition(VuoPoint2d newPosition)
103 {
104  position = newPosition;
105 }
106 
111 {
112  width = newWidth;
113 }
114 
119 {
120  anchor = newAnchor;
121 }
122 
127 {
128  if (state->text == text)
129  return;
130 
131  if(state->text != NULL)
132  VuoRelease(state->text);
133 
134  state->text = VuoTextEdit_truncateLines(text, lineCount);
135 
136  VuoRetain(state->text);
137 }
138 
143 {
144  return VuoText_make(state->text);
145 }
146 
151 {
152  if(state->placeholder != NULL)
153  VuoRelease(state->placeholder);
154  state->placeholder = VuoText_make(text);
155  VuoRetain(state->placeholder);
156 }
157 
162 {
163  VuoRetain(newTheme);
164  VuoRelease(theme);
165  theme = newTheme;
166 }
167 
172 void VuoTextFieldInternal::SetValidateCharInputCallback(bool (*validateCharInputCallback)(const VuoText current, uint32_t newChar, uint16_t position))
173 {
174  validateCharInput = validateCharInputCallback;
175 }
176 
181 void VuoTextFieldInternal::SetValidateTextInputCallback(bool (*validateTextInputCallback)(void *context, const VuoText current, VuoText* modifiedText))
182 {
183  validateTextInput = validateTextInputCallback;
184 }
185 
189 void VuoTextFieldInternal::setSessionEndedCallback(VuoTextFieldInternal::SessionEndedCallbackType sessionEndedCallback)
190 {
191  this->sessionEndedCallback = sessionEndedCallback;
192 }
193 
197 bool VuoTextFieldInternal::FindTextLayer(const VuoRenderedLayers* renderedLayers, uint64_t id, VuoSceneObject* textLayer, VuoList_VuoSceneObject ancestors)
198 {
199  VuoSceneObject foundObject;
200 
201  if( VuoRenderedLayers_findLayerId(*renderedLayers, id, ancestors, &foundObject) )
202  {
203  bool foundTextLayer = false;
204  VuoList_VuoSceneObject childObjects = VuoSceneObject_getChildObjects(foundObject);
205  unsigned long childObjectCount = VuoListGetCount_VuoSceneObject(childObjects);
206  for (unsigned long i = 1; i <= childObjectCount && !foundTextLayer; ++i)
207  {
208  VuoSceneObject child = VuoListGetValue_VuoSceneObject(childObjects, i);
209 
210  if (VuoText_areEqual("Text", VuoSceneObject_getName(child)))
211  {
212  VuoListAppendValue_VuoSceneObject(ancestors, foundObject);
213  *textLayer = child;
214  return true;
215  }
216  }
217  }
218 
219  return false;
220 }
221 
225 bool VuoTextFieldInternal::GetTextLocalPosition(const VuoRenderedLayers* renderedLayers, uint64_t id, VuoPoint2d point, VuoPoint2d* inverseTransformedPoint)
226 {
227  VuoSceneObject textLayer;
229  VuoLocal(ancestors);
230 
231  if( FindTextLayer(renderedLayers, id, &textLayer, ancestors) )
232  {
233  // transform mouse position to text layer space
234  return VuoRenderedLayers_getInverseTransformedPoint(*renderedLayers, ancestors, textLayer, point, inverseTransformedPoint);
235  }
236 
237  return false;
238 }
239 
244 {
245  if(!state->isFocused)
246  return;
247 
248  size_t utf32_len;
249 
250  uint32_t* unicode = VuoText_getUtf32Values(character, &utf32_len);
251 
252  if(utf32_len != 1)
253  {
254  if(unicode != NULL)
255  free(unicode);
256 
257  return;
258  }
259 
260  uint32_t utf32_char = unicode[0];
261 
262  free(unicode);
263 
264  // VLog("char: 0x%x mod: 0x%x = 0x%llx", unicode[0], modifiers, VuoTextEdit_combineKeyAndModifier(unicode[0], modifiers));
265 
266  // End the editing session when Esc or Enter is pressed,
267  // or when Return or Tab are pressed in a single-line textfield.
268  if (utf32_char == KEYCODE_ESC
269  || (modifiers == 0 && utf32_char == KEYCODE_ENTER)
270  || (lineCount <= 1 && utf32_char == KEYCODE_RETURN)
271  || (lineCount <= 1 && utf32_char == KEYCODE_TAB))
272  {
273  state->isFocused = false;
274  OnLostFocus();
275  }
276  else if(utf32_char == KEYCODE_CUT)
277  {
278  VuoTextEdit_cut(&state->text, &state->textEditState);
279  }
280  else if(utf32_char == KEYCODE_COPY)
281  {
282  VuoTextEdit_copy(&state->text, &state->textEditState);
283  }
284  else if(utf32_char == KEYCODE_PASTE)
285  {
286  VuoTextEdit_paste(&state->text, &state->textEditState);
287  }
288  else
289  {
290  // select_start can be greater if placeholder text is present.
291  int cursor = MIN(VuoText_length(state->text), state->textEditState.select_start);
292  if(validateCharInput == NULL || utf32_char < 32 || validateCharInput(state->text, utf32_char, cursor))
293  stb_textedit_key(&state->text, &state->textEditState, VuoTextEdit_combineKeyAndModifier(utf32_char, modifiers));
294  }
295 }
296 
304 {
305  bool stateDidChange = false;
306 
307  if(renderedLayers == NULL)
308  return stateDidChange;
309 
310  unsigned long int pixelsWide, pixelsHigh;
311  float backingScaleFactor;
312  if (VuoRenderedLayers_getRenderingDimensions(*renderedLayers, &pixelsWide, &pixelsHigh, &backingScaleFactor)
313  && (screenSize.x != pixelsWide
314  || screenSize.y != pixelsHigh
315  || screenBackingScaleFactor != backingScaleFactor))
316  stateDidChange = true;
317 
318  screenSize.x = pixelsWide;
319  screenSize.y = pixelsHigh;
320  screenBackingScaleFactor = backingScaleFactor;
321 
322  VuoList_VuoInteraction interactions = VuoRenderedLayers_getInteractions(*renderedLayers);
323  if (VuoListGetCount_VuoInteraction(interactions) > 0)
324  {
325  bool wasHovering = state->isHovering;
326  bool wasFocused = state->isFocused;
327 
328  // @todo currently just registers last interaction.
329  for(int i = 1; i <= VuoListGetCount_VuoInteraction(interactions); i++)
330  {
331  VuoInteraction it = VuoListGetValue_VuoInteraction(interactions, i);
332 
333  bool isPointInLayer = VuoRenderedLayers_isPointInLayerId(*renderedLayers, id, it.position);
334  state->isHovering = isPointInLayer;
335  VuoPoint2d inverseTransformedPoint;
336 
337  if(it.type == VuoInteractionType_Press || it.type == VuoInteractionType_Click)
338  {
339  state->isFocused = isPointInLayer;
340 
341  if( state->isFocused && GetTextLocalPosition(renderedLayers, id, it.position, &inverseTransformedPoint) )
342  {
343  if( it.clickCount == 1 )
344  stb_textedit_click(&state->text, &state->textEditState, inverseTransformedPoint.x, inverseTransformedPoint.y);
345  else if( it.clickCount == 2 )
347  else if( it.clickCount == 3 )
349 
350  stateDidChange = true;
351  }
352  }
353  else if(state->isFocused && it.type == VuoInteractionType_Drag)
354  {
355  if( GetTextLocalPosition(renderedLayers, id, it.position, &inverseTransformedPoint) )
356  stateDidChange = stb_textedit_drag(&state->text, &state->textEditState, inverseTransformedPoint.x, inverseTransformedPoint.y);
357  }
358  }
359 
360  if(state->isHovering != wasHovering || state->isFocused != wasFocused)
361  {
362  if(!state->isFocused && wasFocused)
363  OnLostFocus();
364 
365  stateDidChange = true;
366  }
367  }
368 
369  return stateDidChange;
370 }
371 
375 void VuoTextFieldInternal::OnLostFocus()
376 {
377  if(validateTextInput != NULL)
378  {
379  VuoText modifiedText = NULL;
380 
381  if(validateTextInput(context, state->text, &modifiedText))
382  {
383  SetText(modifiedText);
384 
385  if(modifiedText != NULL)
386  {
387  VuoRetain(modifiedText);
388  VuoRelease(modifiedText);
389  }
390  }
391  }
392 
393  if (sessionEndedCallback)
394  sessionEndedCallback(context, state->text);
395 }
396 
401 {
402  if(state->textImageData)
403  {
404  VuoRelease(state->textImageData);
405  state->textImageData = NULL;
406  state->textEditState.textImageData = NULL;
407  }
408 
409  VuoUiThemeTextField* textTheme = static_cast<VuoUiThemeTextField*>(VuoUiTheme_getSpecificTheme(theme, "VuoUiThemeTextField"));
410  VuoLocal(textTheme);
411 
412  VuoLayer layer = textTheme->render(screenSize,
413  screenBackingScaleFactor,
414  state->text,
415  state->placeholder,
416  lineCount,
417  state->textEditState.cursor,
418  state->textEditState.select_start,
419  state->textEditState.select_end,
420  position,
421  width,
422  anchor,
423  state->isHovering,
424  state->isFocused,
425  &state->textImageData);
426 
427  if(state->textImageData)
428  {
429  VuoRetain(state->textImageData);
430  state->textEditState.textImageData = state->textImageData;
431  }
432 
433  VuoLayer_setId(layer, id);
434 
435  return layer;
436 }