29static String substring (
const String& text, Range<int> range)
31 return text.
substring (range.getStart(), range.getEnd());
34TextLayout::Glyph::Glyph (
int glyph, Point<float>
anch,
float w) noexcept
35 : glyphCode (glyph), anchor (
anch), width (w)
43 glyphs.ensureStorageAllocated (numGlyphsToPreallocate);
51 for (
auto& glyph : glyphs)
53 Range<float> r (glyph.anchor.x, glyph.anchor.x + glyph.width);
71 float lead,
int numRunsToPreallocate)
72 : stringRange (range), lineOrigin (o),
73 ascent (asc), descent (desc), leading (lead)
79 : stringRange (
other.stringRange), lineOrigin (
other.lineOrigin),
82 runs.addCopiesOf (
other.runs);
85TextLayout::Line& TextLayout::Line::operator= (
const Line&
other)
97 for (
auto* run : runs)
99 auto runRange = run->getRunBoundsX();
112 return range + lineOrigin.x;
117 return { lineOrigin.y - ascent,
118 lineOrigin.y + descent };
123 auto x = getLineBoundsX();
124 auto y = getLineBoundsY();
126 return { x.getStart(), y.getStart(), x.getLength(), y.getLength() };
129void TextLayout::Line::swap (
Line&
other)
noexcept
141 : width (0), height (0), justification (
Justification::topLeft)
147 justification (
other.justification)
149 lines.addCopiesOf (
other.lines);
153 : lines (std::move (
other.lines)),
155 justification (
other.justification)
159TextLayout& TextLayout::operator= (TextLayout&&
other)
noexcept
161 lines = std::move (
other.lines);
163 height =
other.height;
164 justification =
other.justification;
168TextLayout& TextLayout::operator= (
const TextLayout&
other)
171 height =
other.height;
172 justification =
other.justification;
174 lines.addCopiesOf (
other.lines);
184 return *lines.getUnchecked (index);
201 auto& context = g.getInternalContext();
204 auto clip = context.getClipBounds();
208 for (
auto& line : *
this)
218 auto lineOrigin =
origin + line.lineOrigin;
220 for (
auto* run : line.runs)
222 context.setFont (run->font);
223 context.setFill (run->colour);
225 for (
auto& glyph : run->glyphs)
227 lineOrigin.y + glyph.anchor.y));
229 if (run->font.isUnderlined())
240 context.restoreState();
255 if (! createNativeLayout (text))
256 createStandardLayout (text);
268 auto minimumWidth = maxWidth / 2.0f;
269 auto bestWidth = maxWidth;
272 while (maxWidth > minimumWidth)
279 auto line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
280 auto line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
291 bestWidth = maxWidth;
302namespace TextLayoutHelpers
307 : text (t), font (f), colour (c),
308 area (font.getStringWidthFloat (t), f.
getHeight()),
309 isWhitespace (whitespace),
319 const bool isWhitespace, isNewLine;
344 for (
int i = 0; i < tokens.size(); ++i)
346 auto& t = *tokens.getUnchecked (i);
350 t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text),
newGlyphs,
xOffsets);
362 auto tokenOrigin = t.area.getPosition().translated (0, t.font.getAscent());
381 if (
auto* nextToken = tokens[i + 1])
383 if (t.font != nextToken->font || t.colour != nextToken->colour)
389 if (t.line != nextToken->line)
392 currentRun = std::make_unique<TextLayout::Run>();
422 for (
auto& line : layout)
424 auto dx =
totalW - line.getLineBoundsX().getLength();
429 line.lineOrigin.x +=
dx;
446 static int getCharacterType (
juce_wchar c)
noexcept
448 if (c ==
'\r' || c ==
'\n')
462 auto c = t.getAndAdvance();
467 auto charType = getCharacterType (c);
477 if (c ==
'\r' && *t ==
'\n')
494 float x = 0, y = 0, h = 0;
497 for (i = 0; i < tokens.size(); ++i)
499 auto& t = *tokens.getUnchecked (i);
514 setLastLineHeight (i + 1, h);
522 setLastLineHeight (
jmin (i + 1, tokens.size()), h);
526 void setLastLineHeight (
int i,
float height)
noexcept
530 auto&
tok = *tokens.getUnchecked (i);
532 if (
tok.line == totalLines)
533 tok.lineHeight = height;
548 appendText (substring (text.
getText(), attr.range),
549 attr.font, attr.colour);
553 static String getTrimmedEndIfNotAllWhitespace (
const String& s)
574 l.createLayout (text, *
this);
579 if (! lines.isEmpty())
581 auto bounds = lines.getFirst()->getLineBounds();
583 for (
auto* line : lines)
584 bounds = bounds.getUnion (line->getLineBounds());
586 for (
auto* line : lines)
587 line->lineOrigin.x -= bounds.getX();
589 width = bounds.getWidth();
590 height = bounds.getHeight();
Holds a resizable array of primitive or copy-by-value objects.
A text string with a set of colour/font settings that are associated with sub-ranges of the text.
WordWrap getWordWrap() const noexcept
Returns the word-wrapping behaviour.
float getLineSpacing() const noexcept
Returns the extra line-spacing distance.
const Attribute & getAttribute(int index) const noexcept
Returns one of the string's attributes.
int getNumAttributes() const noexcept
Returns the number of attributes that have been added to this string.
WordWrap
Types of word-wrap behaviour.
@ none
No word-wrapping: lines extend indefinitely.
const String & getText() const noexcept
Returns the complete text of this attributed string.
Justification getJustification() const noexcept
Returns the justification that should be used for laying-out the text.
static bool isWhitespace(char character) noexcept
Checks whether a character is whitespace.
Represents a colour, also including a transparency value.
Represents a particular font, including its size, style, etc.
float getDescent() const
Returns the amount that the font descends below its baseline, in pixels.
float getHeight() const noexcept
Returns the total height of this font, in pixels.
float getAscent() const
Returns the height of the font above its baseline, in pixels.
A graphics context, used for drawing a component or image.
Represents a type of justification to be used when positioning graphical items.
@ horizontallyCentred
Indicates that the item should be placed in the centre between the left and right sides of the availa...
@ right
Indicates that the item should be aligned against the right edge of the available space.
const Rectangle< ValueType > appliedToRectangle(const Rectangle< ValueType > &areaToAdjust, const Rectangle< ValueType > &targetSpace) const noexcept
Returns the new position of a rectangle that has been justified to fit within a given space.
int getFlags() const noexcept
Returns the raw flags that are set for this Justification object.
An array designed for holding objects.
A pair of (x, y) coordinates.
A general-purpose range object, that simply represents any linear range with a start and end point.
constexpr Range getUnionWith(Range other) const noexcept
Returns the smallest range that contains both this one and the other one.
Manages a rectangle and allows geometric operations to be performed on it.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
void setPosition(Point< ValueType > newPos) noexcept
Changes the position of the rectangle's top-left corner (leaving its size unchanged).
ValueType getHeight() const noexcept
Returns the height of the rectangle.
bool containsChar(juce_wchar character) const noexcept
Tests whether the string contains a particular character.
String trimEnd() const
Returns a copy of this string with any whitespace characters removed from the end.
String replaceCharacters(StringRef charactersToReplace, StringRef charactersToInsertInstead) const
Replaces a set of characters with another set.
static String charToString(juce_wchar character)
Creates a string from a single character.
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
A line containing a sequence of glyph-runs.
Range< float > getLineBoundsY() const noexcept
Returns the Y position range which contains all the glyphs in this line.
Rectangle< float > getLineBounds() const noexcept
Returns the smallest rectangle which contains all the glyphs in this line.
Range< float > getLineBoundsX() const noexcept
Returns the X position range which contains all the glyphs in this line.
A sequence of glyphs with a common font and colour.
A Pre-formatted piece of text, which may contain multiple fonts and colours.
void recalculateSize()
If you modify the TextLayout after creating it, call this to compute the new dimensions of the conten...
void createLayoutWithBalancedLineLengths(const AttributedString &, float maxWidth)
Creates a layout, attempting to choose a width which results in lines of a similar length.
int getNumLines() const noexcept
Returns the number of lines in the layout.
float getWidth() const noexcept
Returns the maximum width of the content.
void ensureStorageAllocated(int numLinesNeeded)
Pre-allocates space for the specified number of lines.
void addLine(std::unique_ptr< Line >)
Adds a line to the layout.
void draw(Graphics &, Rectangle< float > area) const
Draws the layout within the specified area.
float getHeight() const noexcept
Returns the maximum height of the content.
void createLayout(const AttributedString &, float maxWidth)
Creates a layout from the given attributed string.
TextLayout()
Creates an empty layout.
Line & getLine(int index) const noexcept
Returns one of the lines.
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.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...