Vuo  2.4.0
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
17extern "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{
55private:
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
73public:
74 static std::string type;
75
80 {
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
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
488 }
489#endif
490
491 if (hasText || hasPlaceholder)
492 {
493 for(unsigned int i = 0; i < VuoListGetCount_VuoLayer(highlights); i++)
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}