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.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88