JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_GlyphArrangement.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29static constexpr bool isNonBreakingSpace (const juce_wchar c)
30{
31 return c == 0x00a0
32 || c == 0x2007
33 || c == 0x202f
34 || c == 0x2060;
35}
36
37PositionedGlyph::PositionedGlyph() noexcept
38 : character (0), glyph (0), x (0), y (0), w (0), whitespace (false)
39{
40}
41
42PositionedGlyph::PositionedGlyph (const Font& font_, juce_wchar character_, int glyphNumber,
43 float anchorX, float baselineY, float width, bool whitespace_)
44 : font (font_), character (character_), glyph (glyphNumber),
45 x (anchorX), y (baselineY), w (width), whitespace (whitespace_)
46{
47}
48
49static void drawGlyphWithFont (Graphics& g, int glyph, const Font& font, AffineTransform t)
50{
51 auto& context = g.getInternalContext();
52 context.setFont (font);
53 context.drawGlyph (glyph, t);
54}
55
56void PositionedGlyph::draw (Graphics& g) const
57{
58 if (! isWhitespace())
59 drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y));
60}
61
62void PositionedGlyph::draw (Graphics& g, AffineTransform transform) const
63{
64 if (! isWhitespace())
65 drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform));
66}
67
68void PositionedGlyph::createPath (Path& path) const
69{
70 if (! isWhitespace())
71 {
72 if (auto t = font.getTypefacePtr())
73 {
74 Path p;
75 t->getOutlineForGlyph (glyph, p);
76
77 path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight())
78 .translated (x, y));
79 }
80 }
81}
82
83bool PositionedGlyph::hitTest (float px, float py) const
84{
85 if (getBounds().contains (px, py) && ! isWhitespace())
86 {
87 if (auto t = font.getTypefacePtr())
88 {
89 Path p;
90 t->getOutlineForGlyph (glyph, p);
91
92 AffineTransform::translation (-x, -y)
93 .scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight())
94 .transformPoint (px, py);
95
96 return p.contains (px, py);
97 }
98 }
99
100 return false;
101}
102
103void PositionedGlyph::moveBy (float deltaX, float deltaY)
104{
105 x += deltaX;
106 y += deltaY;
107}
108
109
110//==============================================================================
111GlyphArrangement::GlyphArrangement()
112{
113 glyphs.ensureStorageAllocated (128);
114}
115
116//==============================================================================
117void GlyphArrangement::clear()
118{
119 glyphs.clear();
120}
121
122PositionedGlyph& GlyphArrangement::getGlyph (int index) noexcept
123{
124 return glyphs.getReference (index);
125}
126
127//==============================================================================
128void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other)
129{
130 glyphs.addArray (other.glyphs);
131}
132
133void GlyphArrangement::addGlyph (const PositionedGlyph& glyph)
134{
135 glyphs.add (glyph);
136}
137
138void GlyphArrangement::removeRangeOfGlyphs (int startIndex, int num)
139{
140 glyphs.removeRange (startIndex, num < 0 ? glyphs.size() : num);
141}
142
143//==============================================================================
144void GlyphArrangement::addLineOfText (const Font& font, const String& text, float xOffset, float yOffset)
145{
146 addCurtailedLineOfText (font, text, xOffset, yOffset, 1.0e10f, false);
147}
148
149void GlyphArrangement::addCurtailedLineOfText (const Font& font, const String& text,
150 float xOffset, float yOffset,
151 float maxWidthPixels, bool useEllipsis)
152{
153 if (text.isNotEmpty())
154 {
155 Array<int> newGlyphs;
156 Array<float> xOffsets;
157 font.getGlyphPositions (text, newGlyphs, xOffsets);
158 auto textLen = newGlyphs.size();
159 glyphs.ensureStorageAllocated (glyphs.size() + textLen);
160
161 auto t = text.getCharPointer();
162
163 for (int i = 0; i < textLen; ++i)
164 {
165 auto nextX = xOffsets.getUnchecked (i + 1);
166
167 if (nextX > maxWidthPixels + 1.0f)
168 {
169 // curtail the string if it's too wide..
170 if (useEllipsis && textLen > 3 && glyphs.size() >= 3)
171 insertEllipsis (font, xOffset + maxWidthPixels, 0, glyphs.size());
172
173 break;
174 }
175
176 auto thisX = xOffsets.getUnchecked (i);
177 auto isWhitespace = isNonBreakingSpace (*t) || t.isWhitespace();
178
179 glyphs.add (PositionedGlyph (font, t.getAndAdvance(),
180 newGlyphs.getUnchecked (i),
181 xOffset + thisX, yOffset,
182 nextX - thisX, isWhitespace));
183 }
184 }
185}
186
187int GlyphArrangement::insertEllipsis (const Font& font, float maxXPos, int startIndex, int endIndex)
188{
189 int numDeleted = 0;
190
191 if (! glyphs.isEmpty())
192 {
193 Array<int> dotGlyphs;
194 Array<float> dotXs;
195 font.getGlyphPositions ("..", dotGlyphs, dotXs);
196
197 auto dx = dotXs[1];
198 float xOffset = 0.0f, yOffset = 0.0f;
199
200 while (endIndex > startIndex)
201 {
202 auto& pg = glyphs.getReference (--endIndex);
203 xOffset = pg.x;
204 yOffset = pg.y;
205
206 glyphs.remove (endIndex);
207 ++numDeleted;
208
209 if (xOffset + dx * 3 <= maxXPos)
210 break;
211 }
212
213 for (int i = 3; --i >= 0;)
214 {
215 glyphs.insert (endIndex++, PositionedGlyph (font, '.', dotGlyphs.getFirst(),
216 xOffset, yOffset, dx, false));
217 --numDeleted;
218 xOffset += dx;
219
220 if (xOffset > maxXPos)
221 break;
222 }
223 }
224
225 return numDeleted;
226}
227
228void GlyphArrangement::addJustifiedText (const Font& font, const String& text,
229 float x, float y, float maxLineWidth,
230 Justification horizontalLayout,
231 float leading)
232{
233 auto lineStartIndex = glyphs.size();
234 addLineOfText (font, text, x, y);
235
236 auto originalY = y;
237
238 while (lineStartIndex < glyphs.size())
239 {
240 int i = lineStartIndex;
241
242 if (glyphs.getReference (i).getCharacter() != '\n'
243 && glyphs.getReference (i).getCharacter() != '\r')
244 ++i;
245
246 auto lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth;
247 int lastWordBreakIndex = -1;
248
249 while (i < glyphs.size())
250 {
251 auto& pg = glyphs.getReference (i);
252 auto c = pg.getCharacter();
253
254 if (c == '\r' || c == '\n')
255 {
256 ++i;
257
258 if (c == '\r' && i < glyphs.size()
259 && glyphs.getReference (i).getCharacter() == '\n')
260 ++i;
261
262 break;
263 }
264
265 if (pg.isWhitespace())
266 {
267 lastWordBreakIndex = i + 1;
268 }
269 else if (pg.getRight() - 0.0001f >= lineMaxX)
270 {
271 if (lastWordBreakIndex >= 0)
272 i = lastWordBreakIndex;
273
274 break;
275 }
276
277 ++i;
278 }
279
280 auto currentLineStartX = glyphs.getReference (lineStartIndex).getLeft();
281 auto currentLineEndX = currentLineStartX;
282
283 for (int j = i; --j >= lineStartIndex;)
284 {
285 if (! glyphs.getReference (j).isWhitespace())
286 {
287 currentLineEndX = glyphs.getReference (j).getRight();
288 break;
289 }
290 }
291
292 float deltaX = 0.0f;
293
294 if (horizontalLayout.testFlags (Justification::horizontallyJustified))
295 spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth);
296 else if (horizontalLayout.testFlags (Justification::horizontallyCentred))
297 deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f;
298 else if (horizontalLayout.testFlags (Justification::right))
299 deltaX = maxLineWidth - (currentLineEndX - currentLineStartX);
300
301 moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex,
302 x + deltaX - currentLineStartX, y - originalY);
303
304 lineStartIndex = i;
305
306 y += font.getHeight() + leading;
307 }
308}
309
310void GlyphArrangement::addFittedText (const Font& f, const String& text,
311 float x, float y, float width, float height,
312 Justification layout, int maximumLines,
313 float minimumHorizontalScale)
314{
315 if (approximatelyEqual (minimumHorizontalScale, 0.0f))
316 minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor();
317
318 // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0
319 jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f);
320
321 if (text.containsAnyOf ("\r\n"))
322 {
323 addLinesWithLineBreaks (text, f, x, y, width, height, layout);
324 }
325 else
326 {
327 auto startIndex = glyphs.size();
328 auto trimmed = text.trim();
329 addLineOfText (f, trimmed, x, y);
330 auto numGlyphs = glyphs.size() - startIndex;
331
332 if (numGlyphs > 0)
333 {
334 auto lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
335 - glyphs.getReference (startIndex).getLeft();
336
337 if (lineWidth > 0)
338 {
339 if (lineWidth * minimumHorizontalScale < width)
340 {
341 if (lineWidth > width)
342 stretchRangeOfGlyphs (startIndex, numGlyphs, width / lineWidth);
343
344 justifyGlyphs (startIndex, numGlyphs, x, y, width, height, layout);
345 }
346 else if (maximumLines <= 1)
347 {
348 fitLineIntoSpace (startIndex, numGlyphs, x, y, width, height,
349 f, layout, minimumHorizontalScale);
350 }
351 else
352 {
353 splitLines (trimmed, f, startIndex, x, y, width, height,
354 maximumLines, lineWidth, layout, minimumHorizontalScale);
355 }
356 }
357 }
358 }
359}
360
361//==============================================================================
362void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float dx, const float dy)
363{
364 jassert (startIndex >= 0);
365
366 if (! approximatelyEqual (dx, 0.0f) || ! approximatelyEqual (dy, 0.0f))
367 {
368 if (num < 0 || startIndex + num > glyphs.size())
369 num = glyphs.size() - startIndex;
370
371 while (--num >= 0)
372 glyphs.getReference (startIndex++).moveBy (dx, dy);
373 }
374}
375
376void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f,
377 float x, float y, float width, float height, Justification layout)
378{
380 ga.addJustifiedText (f, text, x, y, width, layout);
381
382 auto bb = ga.getBoundingBox (0, -1, false);
383 auto dy = y - bb.getY();
384
385 if (layout.testFlags (Justification::verticallyCentred)) dy += (height - bb.getHeight()) * 0.5f;
386 else if (layout.testFlags (Justification::bottom)) dy += (height - bb.getHeight());
387
388 ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);
389
390 glyphs.addArray (ga.glyphs);
391}
392
393int GlyphArrangement::fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font& font,
394 Justification justification, float minimumHorizontalScale)
395{
396 int numDeleted = 0;
397 auto lineStartX = glyphs.getReference (start).getLeft();
398 auto lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX;
399
400 if (lineWidth > w)
401 {
402 if (minimumHorizontalScale < 1.0f)
403 {
404 stretchRangeOfGlyphs (start, numGlyphs, jmax (minimumHorizontalScale, w / lineWidth));
405 lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX - 0.5f;
406 }
407
408 if (lineWidth > w)
409 {
410 numDeleted = insertEllipsis (font, lineStartX + w, start, start + numGlyphs);
411 numGlyphs -= numDeleted;
412 }
413 }
414
415 justifyGlyphs (start, numGlyphs, x, y, w, h, justification);
416 return numDeleted;
417}
418
419void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, float horizontalScaleFactor)
420{
421 jassert (startIndex >= 0);
422
423 if (num < 0 || startIndex + num > glyphs.size())
424 num = glyphs.size() - startIndex;
425
426 if (num > 0)
427 {
428 auto xAnchor = glyphs.getReference (startIndex).getLeft();
429
430 while (--num >= 0)
431 {
432 auto& pg = glyphs.getReference (startIndex++);
433
434 pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor;
435 pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor);
436 pg.w *= horizontalScaleFactor;
437 }
438 }
439}
440
441Rectangle<float> GlyphArrangement::getBoundingBox (int startIndex, int num, bool includeWhitespace) const
442{
443 jassert (startIndex >= 0);
444
445 if (num < 0 || startIndex + num > glyphs.size())
446 num = glyphs.size() - startIndex;
447
448 Rectangle<float> result;
449
450 while (--num >= 0)
451 {
452 auto& pg = glyphs.getReference (startIndex++);
453
454 if (includeWhitespace || ! pg.isWhitespace())
455 result = result.getUnion (pg.getBounds());
456 }
457
458 return result;
459}
460
461void GlyphArrangement::justifyGlyphs (int startIndex, int num,
462 float x, float y, float width, float height,
463 Justification justification)
464{
465 jassert (num >= 0 && startIndex >= 0);
466
467 if (glyphs.size() > 0 && num > 0)
468 {
469 auto bb = getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified
470 | Justification::horizontallyCentred));
471 float deltaX = x, deltaY = y;
472
473 if (justification.testFlags (Justification::horizontallyJustified)) deltaX -= bb.getX();
474 else if (justification.testFlags (Justification::horizontallyCentred)) deltaX += (width - bb.getWidth()) * 0.5f - bb.getX();
475 else if (justification.testFlags (Justification::right)) deltaX += width - bb.getRight();
476 else deltaX -= bb.getX();
477
478 if (justification.testFlags (Justification::top)) deltaY -= bb.getY();
479 else if (justification.testFlags (Justification::bottom)) deltaY += height - bb.getBottom();
480 else deltaY += (height - bb.getHeight()) * 0.5f - bb.getY();
481
482 moveRangeOfGlyphs (startIndex, num, deltaX, deltaY);
483
484 if (justification.testFlags (Justification::horizontallyJustified))
485 {
486 int lineStart = 0;
487 auto baseY = glyphs.getReference (startIndex).getBaselineY();
488
489 int i;
490 for (i = 0; i < num; ++i)
491 {
492 auto glyphY = glyphs.getReference (startIndex + i).getBaselineY();
493
494 if (! approximatelyEqual (glyphY, baseY))
495 {
496 spreadOutLine (startIndex + lineStart, i - lineStart, width);
497
498 lineStart = i;
499 baseY = glyphY;
500 }
501 }
502
503 if (i > lineStart)
504 spreadOutLine (startIndex + lineStart, i - lineStart, width);
505 }
506 }
507}
508
509void GlyphArrangement::spreadOutLine (int start, int num, float targetWidth)
510{
511 if (start + num < glyphs.size()
512 && glyphs.getReference (start + num - 1).getCharacter() != '\r'
513 && glyphs.getReference (start + num - 1).getCharacter() != '\n')
514 {
515 int numSpaces = 0;
516 int spacesAtEnd = 0;
517
518 for (int i = 0; i < num; ++i)
519 {
520 if (glyphs.getReference (start + i).isWhitespace())
521 {
522 ++spacesAtEnd;
523 ++numSpaces;
524 }
525 else
526 {
527 spacesAtEnd = 0;
528 }
529 }
530
531 numSpaces -= spacesAtEnd;
532
533 if (numSpaces > 0)
534 {
535 auto startX = glyphs.getReference (start).getLeft();
536 auto endX = glyphs.getReference (start + num - 1 - spacesAtEnd).getRight();
537
538 auto extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces;
539 float deltaX = 0.0f;
540
541 for (int i = 0; i < num; ++i)
542 {
543 glyphs.getReference (start + i).moveBy (deltaX, 0.0f);
544
545 if (glyphs.getReference (start + i).isWhitespace())
546 deltaX += extraPaddingBetweenWords;
547 }
548 }
549 }
550}
551
552static bool isBreakableGlyph (const PositionedGlyph& g) noexcept
553{
554 return ! isNonBreakingSpace (g.getCharacter()) && (g.isWhitespace() || g.getCharacter() == '-');
555}
556
557void GlyphArrangement::splitLines (const String& text, Font font, int startIndex,
558 float x, float y, float width, float height, int maximumLines,
559 float lineWidth, Justification layout, float minimumHorizontalScale)
560{
561 auto length = text.length();
562 auto originalStartIndex = startIndex;
563 int numLines = 1;
564
565 if (length <= 12 && ! text.containsAnyOf (" -\t\r\n"))
566 maximumLines = 1;
567
568 maximumLines = jmin (maximumLines, length);
569
570 while (numLines < maximumLines)
571 {
572 ++numLines;
573 auto newFontHeight = height / (float) numLines;
574
575 if (newFontHeight < font.getHeight())
576 {
577 font.setHeight (jmax (8.0f, newFontHeight));
578
579 removeRangeOfGlyphs (startIndex, -1);
580 addLineOfText (font, text, x, y);
581
582 lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
583 - glyphs.getReference (startIndex).getLeft();
584 }
585
586 // Try to estimate the point at which there are enough lines to fit the text,
587 // allowing for unevenness in the lengths due to differently sized words.
588 const float lineLengthUnevennessAllowance = 80.0f;
589
590 if ((float) numLines > (lineWidth + lineLengthUnevennessAllowance) / width || newFontHeight < 8.0f)
591 break;
592 }
593
594 if (numLines < 1)
595 numLines = 1;
596
597 int lineIndex = 0;
598 auto lineY = y;
599 auto widthPerLine = jmin (width / minimumHorizontalScale,
600 lineWidth / (float) numLines);
601
602 while (lineY < y + height)
603 {
604 auto endIndex = startIndex;
605 auto lineStartX = glyphs.getReference (startIndex).getLeft();
606 auto lineBottomY = lineY + font.getHeight();
607
608 if (lineIndex++ >= numLines - 1
609 || lineBottomY >= y + height)
610 {
611 widthPerLine = width;
612 endIndex = glyphs.size();
613 }
614 else
615 {
616 while (endIndex < glyphs.size())
617 {
618 if (glyphs.getReference (endIndex).getRight() - lineStartX > widthPerLine)
619 {
620 // got to a point where the line's too long, so skip forward to find a
621 // good place to break it..
622 auto searchStartIndex = endIndex;
623
624 while (endIndex < glyphs.size())
625 {
626 auto& g = glyphs.getReference (endIndex);
627
628 if ((g.getRight() - lineStartX) * minimumHorizontalScale < width)
629 {
630 if (isBreakableGlyph (g))
631 {
632 ++endIndex;
633 break;
634 }
635 }
636 else
637 {
638 // can't find a suitable break, so try looking backwards..
639 endIndex = searchStartIndex;
640
641 for (int back = 1; back < jmin (7, endIndex - startIndex - 1); ++back)
642 {
643 if (isBreakableGlyph (glyphs.getReference (endIndex - back)))
644 {
645 endIndex -= back - 1;
646 break;
647 }
648 }
649
650 break;
651 }
652
653 ++endIndex;
654 }
655
656 break;
657 }
658
659 ++endIndex;
660 }
661
662 auto wsStart = endIndex;
663 auto wsEnd = endIndex;
664
665 while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace())
666 --wsStart;
667
668 while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace())
669 ++wsEnd;
670
671 removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
672 endIndex = jmax (wsStart, startIndex + 1);
673 }
674
675 endIndex -= fitLineIntoSpace (startIndex, endIndex - startIndex,
676 x, lineY, width, font.getHeight(), font,
677 layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
678 minimumHorizontalScale);
679
680 startIndex = endIndex;
681 lineY = lineBottomY;
682
683 if (startIndex >= glyphs.size())
684 break;
685 }
686
687 justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
688 x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
689}
690
691//==============================================================================
692void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg,
693 int i, AffineTransform transform) const
694{
695 auto lineThickness = (pg.font.getDescent()) * 0.3f;
696 auto nextX = pg.x + pg.w;
697
698 if (i < glyphs.size() - 1 && approximatelyEqual (glyphs.getReference (i + 1).y, pg.y))
699 nextX = glyphs.getReference (i + 1).x;
700
701 Path p;
702 p.addRectangle (pg.x, pg.y + lineThickness * 2.0f, nextX - pg.x, lineThickness);
703 g.fillPath (p, transform);
704}
705
706void GlyphArrangement::draw (const Graphics& g) const
707{
708 draw (g, {});
709}
710
711void GlyphArrangement::draw (const Graphics& g, AffineTransform transform) const
712{
713 auto& context = g.getInternalContext();
714 auto lastFont = context.getFont();
715 bool needToRestore = false;
716
717 for (int i = 0; i < glyphs.size(); ++i)
718 {
719 auto& pg = glyphs.getReference (i);
720
721 if (pg.font.isUnderlined())
722 drawGlyphUnderline (g, pg, i, transform);
723
724 if (! pg.isWhitespace())
725 {
726 if (lastFont != pg.font)
727 {
728 lastFont = pg.font;
729
730 if (! needToRestore)
731 {
732 needToRestore = true;
733 context.saveState();
734 }
735
736 context.setFont (lastFont);
737 }
738
739 context.drawGlyph (pg.glyph, AffineTransform::translation (pg.x, pg.y)
740 .followedBy (transform));
741 }
742 }
743
744 if (needToRestore)
745 context.restoreState();
746}
747
748void GlyphArrangement::createPath (Path& path) const
749{
750 for (auto& g : glyphs)
751 g.createPath (path);
752}
753
754int GlyphArrangement::findGlyphIndexAt (float x, float y) const
755{
756 for (int i = 0; i < glyphs.size(); ++i)
757 if (glyphs.getReference (i).hitTest (x, y))
758 return i;
759
760 return -1;
761}
762
763} // namespace juce
Represents a 2D affine-transformation matrix.
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:252
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
ElementType getFirst() const noexcept
Returns the first element in the array, or a default value if the array is empty.
Definition juce_Array.h:290
ElementType & getReference(int index) noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:267
Represents a particular font, including its size, style, etc.
Definition juce_Font.h:42
void getGlyphPositions(const String &text, Array< int > &glyphs, Array< float > &xOffsets) const
Returns the series of glyph numbers and their x offsets needed to represent a string.
float getHeight() const noexcept
Returns the total height of this font, in pixels.
A set of glyphs, each with a position.
Rectangle< float > getBoundingBox(int startIndex, int numGlyphs, bool includeWhitespace) const
Finds the smallest rectangle that will enclose a subset of the glyphs.
void addJustifiedText(const Font &font, const String &text, float x, float y, float maxLineWidth, Justification horizontalLayout, float leading=0.0f)
Adds some multi-line text, breaking lines at word-boundaries if they are too wide.
void moveRangeOfGlyphs(int startIndex, int numGlyphs, float deltaX, float deltaY)
Shifts a set of glyphs by a given amount.
A graphics context, used for drawing a component or image.
Represents a type of justification to be used when positioning graphical items.
bool testFlags(int flagsToTest) const noexcept
Tests a set of flags for this object.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Definition juce_Path.h:65
bool contains(float x, float y, float tolerance=defaultToleranceForTesting) const
Checks whether a point lies within the path.
void addPath(const Path &pathToAppend)
Adds another path to this one.
A glyph from a particular font, with a particular size, style, typeface and position.
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle getUnion(Rectangle other) const noexcept
Returns the smallest rectangle that contains both this one and the one passed-in.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
The JUCE String class!
Definition juce_String.h:53
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end.
bool containsAnyOf(StringRef charactersItMightContain) const noexcept
Looks for any of a set of characters in the string.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
#define jassert(expression)
Platform-independent assertion macro.
typedef float
JUCE Namespace.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.