Vuo  2.3.2
VuoUiThemeTextFieldRounded.cc
Go to the documentation of this file.
1 
11 #define THEME_DEBUG 0
12 
13 #include "VuoUiThemeBase.hh"
14 
15 #include "type.h"
16 
17 extern "C" {
18 #include "VuoImage.h"
19 #include "VuoImageText.h"
20 #include "VuoAnchor.h"
21 #include "VuoPoint2d.h"
22 }
23 
24 #include "VuoUiTheme.h"
25 #include "VuoSceneText.h"
26 #include <vector>
27 
29 #ifdef VUO_COMPILER
31  "title": "UI Theme: Text Field, Rounded",
32  "dependencies": [
33  "VuoBoolean",
34  "VuoColor",
35  "VuoFont",
36  "VuoImage",
37  "VuoImageText",
38  "VuoLayer",
39  "VuoPoint2d",
40  "VuoReal",
41  "VuoAnchor",
42  "VuoHorizontalAlignment",
43  "VuoVerticalAlignment",
44  "VuoSceneText"
45  ],
46  });
47 #endif
49 
54 {
55 private:
56  VuoFont font;
57  VuoAnchor textAnchor;
58  VuoPoint2d textPadding;
59  VuoColor textColor;
60  VuoColor textColorHovered;
61  VuoColor textColorActive;
62  VuoColor backgroundColor;
63  VuoColor backgroundColorHovered;
64  VuoColor backgroundColorActive;
65  VuoColor borderColor;
66  VuoColor borderColorHovered;
67  VuoColor borderColorActive;
68  VuoReal borderThickness;
69  VuoColor cursorColor;
70  VuoColor selectionColor;
71  VuoReal cornerRoundness;
72 
73 public:
74  static std::string type;
75 
80  {
81  return new VuoUiThemeTextFieldRounded(
82  VuoJson_getObjectValue(VuoFont, js, "font", (VuoFont){VuoText_make("Avenir-Medium"), 24, false, (VuoColor){1,1,1,1}, VuoHorizontalAlignment_Left, 1, 1}),
83  VuoJson_getObjectValue(VuoAnchor, js, "textAnchor", VuoAnchor_make(VuoHorizontalAlignment_Left, VuoVerticalAlignment_Top)),
84  VuoJson_getObjectValue(VuoPoint2d, js, "textPadding", (VuoPoint2d){0.02, 0.01}),
85  VuoJson_getObjectValue(VuoColor, js, "textColor", (VuoColor){0, 0, 0, 0.7}),
86  VuoJson_getObjectValue(VuoColor, js, "textColorHovered", (VuoColor){0, 0, 0, 0.8}),
87  VuoJson_getObjectValue(VuoColor, js, "textColorActive", (VuoColor){0, 0, 0, 1}),
88  VuoJson_getObjectValue(VuoColor, js, "backgroundColor", (VuoColor){1, 1, 1, 0.5}),
89  VuoJson_getObjectValue(VuoColor, js, "backgroundColorHovered", (VuoColor){1, 1, 1, 0.6}),
90  VuoJson_getObjectValue(VuoColor, js, "backgroundColorActive", (VuoColor){1, 1, 1, 1}),
91  VuoJson_getObjectValue(VuoColor, js, "borderColor", (VuoColor){.46, .46, .46, 1}),
92  VuoJson_getObjectValue(VuoColor, js, "borderColorHovered", (VuoColor){.46, .48, .49, 1}),
93  VuoJson_getObjectValue(VuoColor, js, "borderColorActive", (VuoColor){.46, .48, 1, 1}),
94  VuoJson_getObjectValue(VuoReal, js, "borderThickness", .005),
95  VuoJson_getObjectValue(VuoColor, js, "cursorColor", (VuoColor){0, 0, 0, 1}),
96  VuoJson_getObjectValue(VuoColor, js, "selectionColor", (VuoColor){.7, .84, 1, 1}),
97  VuoJson_getObjectValue(VuoReal, js, "cornerRoundness", .5));
98  }
99 
104  VuoAnchor _textAnchor,
105  VuoPoint2d _textPadding,
106  VuoColor _textColor,
107  VuoColor _textColorHovered,
108  VuoColor _textColorActive,
109  VuoColor _backgroundColor,
110  VuoColor _backgroundColorHovered,
111  VuoColor _backgroundColorActive,
112  VuoColor _borderColor,
113  VuoColor _borderColorHovered,
114  VuoColor _borderColorActive,
115  VuoReal _borderThickness,
116  VuoColor _cursorColor,
117  VuoColor _selectionColor,
118  VuoReal _cornerRoundness)
119  {
120  font = _font;
121  VuoFont_retain(font);
122  textAnchor = _textAnchor;
123  textPadding = _textPadding;
124  textColor = _textColor;
125  textColorHovered = _textColorHovered;
126  textColorActive = _textColorActive;
127  backgroundColor = _backgroundColor;
128  backgroundColorHovered = _backgroundColorHovered;
129  backgroundColorActive = _backgroundColorActive;
130  borderColor = _borderColor;
131  borderColorHovered = _borderColorHovered;
132  borderColorActive = _borderColorActive;
133  borderThickness = _borderThickness;
134  cursorColor = _cursorColor;
135  selectionColor = _selectionColor;
136  cornerRoundness = _cornerRoundness;
137  }
138 
140  {
141  VuoFont_release(font);
142  }
143 
148  {
150  json_object_object_add(json, "font", VuoFont_getJson(font));
151  json_object_object_add(json, "textAnchor", VuoAnchor_getJson(textAnchor));
152  json_object_object_add(json, "textPadding", VuoPoint2d_getJson(textPadding));
153  json_object_object_add(json, "textColor", VuoColor_getJson(textColor));
154  json_object_object_add(json, "textColorHovered", VuoColor_getJson(textColorHovered));
155  json_object_object_add(json, "textColorActive", VuoColor_getJson(textColorActive));
156  json_object_object_add(json, "backgroundColor", VuoColor_getJson(backgroundColor));
157  json_object_object_add(json, "backgroundColorHovered", VuoColor_getJson(backgroundColorHovered));
158  json_object_object_add(json, "backgroundColorActive", VuoColor_getJson(backgroundColorActive));
159  json_object_object_add(json, "borderColor", VuoColor_getJson(borderColor));
160  json_object_object_add(json, "borderColorHovered", VuoColor_getJson(borderColorHovered));
161  json_object_object_add(json, "borderColorActive", VuoColor_getJson(borderColorActive));
162  json_object_object_add(json, "borderThickness", VuoReal_getJson(borderThickness));
163  json_object_object_add(json, "cursorColor", VuoColor_getJson(cursorColor));
164  json_object_object_add(json, "selectionColor", VuoColor_getJson(selectionColor));
165  json_object_object_add(json, "cornerRoundness", VuoReal_getJson(cornerRoundness));
166  return json;
167  }
168 
172  char *getSummary()
173  {
174  return strdup("Text Field Theme (Rounded)");
175  }
176 
180  bool operator==(const VuoSerializable &that)
181  {
183  return VuoFont_areEqual(font, thatSpecialized->font)
184  && VuoAnchor_areEqual(textAnchor, thatSpecialized->textAnchor)
185  && VuoPoint2d_areEqual(textPadding, thatSpecialized->textPadding)
186  && VuoColor_areEqual(textColor, thatSpecialized->textColor)
187  && VuoColor_areEqual(textColorHovered, thatSpecialized->textColorHovered)
188  && VuoColor_areEqual(textColorActive, thatSpecialized->textColorActive)
189  && VuoColor_areEqual(backgroundColor, thatSpecialized->backgroundColor)
190  && VuoColor_areEqual(backgroundColorHovered, thatSpecialized->backgroundColorHovered)
191  && VuoColor_areEqual(backgroundColorActive, thatSpecialized->backgroundColorActive)
192  && VuoColor_areEqual(borderColor, thatSpecialized->borderColor)
193  && VuoColor_areEqual(borderColorHovered, thatSpecialized->borderColorHovered)
194  && VuoColor_areEqual(borderColorActive, thatSpecialized->borderColorActive)
195  && VuoReal_areEqual(borderThickness, thatSpecialized->borderThickness)
196  && VuoColor_areEqual(cursorColor, thatSpecialized->cursorColor)
197  && VuoColor_areEqual(selectionColor, thatSpecialized->selectionColor)
198  && VuoReal_areEqual(cornerRoundness, thatSpecialized->cornerRoundness);
199  }
200 
204  bool operator<(const VuoSerializable &that)
205  {
207  VuoType_returnInequality(VuoFont, font, thatSpecialized->font);
208  VuoType_returnInequality(VuoAnchor, textAnchor, thatSpecialized->textAnchor);
209  VuoType_returnInequality(VuoPoint2d, textPadding, thatSpecialized->textPadding);
210  VuoType_returnInequality(VuoColor, textColor, thatSpecialized->textColor);
211  VuoType_returnInequality(VuoColor, textColorHovered, thatSpecialized->textColorHovered);
212  VuoType_returnInequality(VuoColor, textColorActive, thatSpecialized->textColorActive);
213  VuoType_returnInequality(VuoColor, backgroundColor, thatSpecialized->backgroundColor);
214  VuoType_returnInequality(VuoColor, backgroundColorHovered, thatSpecialized->backgroundColorHovered);
215  VuoType_returnInequality(VuoColor, backgroundColorActive, thatSpecialized->backgroundColorActive);
216  VuoType_returnInequality(VuoColor, borderColor, thatSpecialized->borderColor);
217  VuoType_returnInequality(VuoColor, borderColorHovered, thatSpecialized->borderColorHovered);
218  VuoType_returnInequality(VuoColor, borderColorActive, thatSpecialized->borderColorActive);
219  VuoType_returnInequality(VuoReal, borderThickness, thatSpecialized->borderThickness);
220  VuoType_returnInequality(VuoColor, cursorColor, thatSpecialized->cursorColor);
221  VuoType_returnInequality(VuoColor, selectionColor, thatSpecialized->selectionColor);
222  VuoType_returnInequality(VuoReal, cornerRoundness, thatSpecialized->cornerRoundness);
223  return false;
224  }
225 
230  VuoLayer render(VuoPoint2d screenSize,
231  VuoReal screenBackingScaleFactor,
232  VuoText text,
233  VuoText placeholderText,
234  int numLines,
235  int cursorIndex,
236  int selectionStart,
237  int selectionEnd,
238  VuoPoint2d position,
239  VuoReal width,
240  VuoAnchor anchor,
241  bool isHovered,
242  bool isFocused,
243  VuoImageTextData* imageTextData)
244  {
246  VuoLocal(layers);
247 
248  VuoLayer textLayer = nullptr;
250  VuoRetain(highlights);
251  VuoLayer textCursor = nullptr;
252 
253  bool hasText = VuoText_length(text) > 0;
254  bool hasPlaceholder = VuoText_length(placeholderText) > 0;
255  VuoText labelText;
256  if (hasText)
257  labelText = text;
258  else if(isFocused || !hasPlaceholder)
259  labelText = VuoText_make("");
260  else
261  labelText = placeholderText;
262  VuoLocal(labelText);
263 
264  float actualWidth = fmax(width, 0.001);
265  float actualHeight;
266 
267  VuoPoint2d textSize = VuoPoint2d_make(0,0);
268 
269  VuoFont f = font;
270  if (isFocused)
271  f.color = (VuoColor){f.color.r * textColorActive.r,
272  f.color.g * textColorActive.g,
273  f.color.b * textColorActive.b,
274  f.color.a * textColorActive.a};
275  else if (isHovered)
276  f.color = (VuoColor){f.color.r * textColorHovered.r,
277  f.color.g * textColorHovered.g,
278  f.color.b * textColorHovered.b,
279  f.color.a * textColorHovered.a};
280  else
281  f.color = (VuoColor){f.color.r * textColor.r,
282  f.color.g * textColor.g,
283  f.color.b * textColor.b,
284  f.color.a * textColor.a};
285 
286  if (!hasText)
287  f.color.a *= .25;
288 
289  VuoImageTextData textData = VuoImage_getTextImageData(labelText, f, screenBackingScaleFactor, 1, 0, true);
290 
291  if(textData)
292  {
293  textData->billboardAnchor = textAnchor;
294  VuoImageTextData_convertToVuoCoordinates(textData, screenSize.x, screenBackingScaleFactor);
295 
296  textSize.x = textData->width;
297  textSize.y = textData->height;
298 
299  // Expand the text field width, if needed, to accommodate long text.
300  // (Scrolling would be preferable, but would take much longer to implement.)
301  actualWidth = fmax(width, textData->width);
302 
303  actualHeight = textData->lineHeight * numLines;
304 
305  VuoSceneObject text = VuoSceneText_make(labelText, f, true, INFINITY, textAnchor);
306  textLayer = (VuoLayer)text;
308  }
309  else
310  {
311  VuoReal lineHeight = VuoImageText_getLineHeight(f, screenSize.x, screenBackingScaleFactor);
312  actualHeight = lineHeight * numLines;
313  textSize.y = lineHeight;
314  }
315 
316 #if THEME_DEBUG
317  VuoList_VuoLayer characterBounds = VuoListCreate_VuoLayer();
318  VuoLocal(characterBounds);
319 
320  if(textData)
321  {
322  for(int i = 0; i < textData->charCount; i++)
323  {
324  // don't draw control characters
325  if(textData->charAdvance[i] < .001)
326  continue;
327 
328  // getPositionForCharIndex returns the bottom left corner of the char billboard
329  unsigned int lineIndex;
330  VuoPoint2d p = VuoImageTextData_getPositionForCharIndex(textData, i, &lineIndex);
331  p.x += textData->charAdvance[i] * .5;
332  p.y += textData->lineHeight * .5;
333 
334  VuoListAppendValue_VuoLayer(characterBounds,
336  VuoColor_makeWithRGBA(.2,.2,.2,.4),
337  p,
338  0,
339  textData->charAdvance[i] * .95,
340  textData->lineHeight * .95));
341  }
342  }
343 #endif
344 
345  // if the text isn't placeholder, also render the cursor and selection (if any)
346  if(isFocused)
347  {
348  VuoPoint2d cursorSize = VuoImageText_getTextSize("|", f, screenSize, screenBackingScaleFactor, false);
349  cursorSize.x *= .5f;
350 
351  VuoPoint2d cursorPosition;
352 
353  if(textData)
354  {
355  cursorPosition = VuoImageTextData_getPositionForCharIndex(textData, cursorIndex, NULL);
356  cursorPosition.y += textData->lineHeight * .5;
357  }
358  else
359  {
360  cursorPosition = VuoPoint2d_make(0, 0);
361  }
362 
363  textCursor = VuoLayer_makeColor(VuoText_make("Text Cursor"),
364  cursorColor,
365  cursorPosition,
366  0,
367  cursorSize.x,
368  cursorSize.y);
369 
370  int textLength = VuoText_length(text);
371  selectionStart = MIN(selectionStart, textLength);
372  selectionEnd = MIN(selectionEnd, textLength);
373 
374  int start = (selectionStart == selectionEnd ? cursorIndex : MIN(selectionStart, selectionEnd));
375  int length = (selectionStart == selectionEnd ? 0 : (MAX(selectionStart, selectionEnd) - start));
376 
377  if (hasText && length > 0)
378  {
379  unsigned int lineCount = 0;
380  VuoRectangle* highlightRects = VuoImageTextData_getRectsForHighlight(textData, start, length, &lineCount);
381 
382  for(int i = 0; i < lineCount; i++)
383  {
385  highlights,
386  VuoLayer_makeColor(VuoText_make(VuoText_format("Text Highlight %i", i)),
387  selectionColor,
388  highlightRects[i].center,
389  0,
390  highlightRects[i].size.x,
391  highlightRects[i].size.y) );
392  }
393 
394  free(highlightRects);
395  }
396  }
397 
398  *imageTextData = textData;
399 
400  actualWidth += textPadding.x * 2;
401  actualHeight += textPadding.y * 2;
402 
403  actualWidth += borderThickness * 2;
404  actualHeight += borderThickness * 2;
405 
406  float outerCornerRoundness = cornerRoundness / numLines;
407  float innerCornerRoundness = (actualHeight - borderThickness * 2 - (actualHeight * (1 - outerCornerRoundness))) / (actualHeight - borderThickness * 2);
408 
409  VuoLayer backgroundLayer = VuoLayer_makeRoundedRectangle( VuoText_make("Text Field Background"),
410  isFocused ? backgroundColorActive : (isHovered ? backgroundColorHovered : backgroundColor),
411  VuoPoint2d_make(0,0),
412  0,
413  actualWidth - borderThickness * 2,
414  actualHeight - borderThickness * 2,
415  1,
416  innerCornerRoundness);
417 
418  VuoLayer borderLayer = VuoLayer_makeRoundedRectangle( VuoText_make("Text Field Border"),
419  isFocused ? borderColorActive : (isHovered ? borderColorHovered : borderColor),
420  VuoPoint2d_make(0,0),
421  0,
422  actualWidth,
423  actualHeight,
424  1,
425  outerCornerRoundness);
426 
427  VuoPoint3d offset = VuoPoint3d_make(0,0,0);
428  VuoPoint3d textOffset = VuoPoint3d_make(0,0,0);
429 
430  if (VuoAnchor_getHorizontal(anchor) == VuoHorizontalAlignment_Left)
431  offset.x = actualWidth * .5f;
432  else if (VuoAnchor_getHorizontal(anchor) == VuoHorizontalAlignment_Right)
433  offset.x = -actualWidth * .5f;
434 
435  if (VuoAnchor_getVertical(anchor) == VuoVerticalAlignment_Top)
436  offset.y = -actualHeight * .5f;
437  else if (VuoAnchor_getVertical(anchor) == VuoVerticalAlignment_Bottom)
438  offset.y = actualHeight * .5f;
439 
440  VuoSceneObject_translate((VuoSceneObject)backgroundLayer, offset);
441  VuoSceneObject_translate((VuoSceneObject)borderLayer, offset);
442 
443  {
444  VuoSceneObject_translate((VuoSceneObject)textLayer, offset);
445  for(auto& highlight : *((std::vector<VuoLayer> *) highlights))
446  VuoSceneObject_translate((VuoSceneObject)highlight, offset);
447  VuoSceneObject_translate((VuoSceneObject)textCursor, offset);
448 
451 
452  if(h != VuoHorizontalAlignment_Center)
453  textOffset.x = ((actualWidth - textPadding.x * 2) * .5) * (h == VuoHorizontalAlignment_Left ? -1 : 1);
454 
455  if(v != VuoVerticalAlignment_Center)
456  textOffset.y = ((actualHeight - textPadding.y * 2) * .5) * (v == VuoVerticalAlignment_Bottom ? -1 : 1);
457 
458  VuoSceneObject_translate((VuoSceneObject)textLayer, textOffset);
459 
460  // the text layer is already transformed by the text renderer to account for font anchor,
461  // so add it after the alignment of text billboard for highlight and cursor
462  if(h != VuoHorizontalAlignment_Center)
463  textOffset.x += (h == VuoHorizontalAlignment_Left ? textSize.x : -textSize.x) * .5;
464 
465  if(v != VuoVerticalAlignment_Center)
466  textOffset.y += (v == VuoVerticalAlignment_Bottom ? textSize.y : -textSize.y) * .5;
467 
468  for(auto& highlight : *((std::vector<VuoLayer> *) highlights))
469  VuoSceneObject_translate((VuoSceneObject)highlight, textOffset);
470 
471  VuoSceneObject_translate((VuoSceneObject)textCursor, textOffset);
472  }
473 
474  VuoListAppendValue_VuoLayer(layers, borderLayer);
475  VuoListAppendValue_VuoLayer(layers, backgroundLayer);
476 
477 #if THEME_DEBUG
478  for(int i = 1; i <= VuoListGetCount_VuoLayer(characterBounds); i++)
479  {
480  VuoLayer l = VuoListGetValue_VuoLayer(characterBounds, i);
481  l.sceneObject.transform.translation = VuoPoint3d_add(l.sceneObject.transform.translation, offset);
482 
483  // VLog("textOffset [%i]: %.2f %.2f", i, textOffset.x, textOffset.y);
484  l.sceneObject.transform.translation.x += textOffset.x;
485  l.sceneObject.transform.translation.y += textOffset.y;
486 
487  VuoListAppendValue_VuoLayer(layers, l);
488  }
489 #endif
490 
491  if (hasText || hasPlaceholder)
492  {
493  for(unsigned int i = 0; i < VuoListGetCount_VuoLayer(highlights); i++)
494  VuoListAppendValue_VuoLayer(layers, VuoListGetValue_VuoLayer(highlights, i+1));
495  VuoListAppendValue_VuoLayer(layers, textLayer);
496  VuoListAppendValue_VuoLayer(layers, textCursor);
497  }
498 
499  VuoRelease(highlights);
500 
501  return VuoLayer_makeGroup(layers, VuoTransform2d_make(position, 0, VuoPoint2d_make(1,1)));
502  }
503 };
504 
506 
511  VuoAnchor textAnchor,
512  VuoPoint2d textPadding,
513  VuoColor textColor,
514  VuoColor textColorHovered,
515  VuoColor textColorActive,
516  VuoColor backgroundColor,
517  VuoColor backgroundColorHovered,
518  VuoColor backgroundColorActive,
519  VuoColor borderColor,
520  VuoColor borderColorHovered,
521  VuoColor borderColorActive,
522  VuoReal borderThickness,
523  VuoColor cursorColor,
524  VuoColor selectionColor,
525  VuoReal cornerRoundness)
526 {
527  return reinterpret_cast<VuoUiTheme>(new VuoUiThemeTextFieldRounded(font,
528  textAnchor,
529  textPadding,
530  textColor,
531  textColorHovered,
532  textColorActive,
533  backgroundColor,
534  backgroundColorHovered,
535  backgroundColorActive,
536  borderColor,
537  borderColorHovered,
538  borderColorActive,
539  borderThickness,
540  cursorColor,
541  selectionColor,
542  cornerRoundness));
543 }