Browse Source

Modernised some code in some text layout classes, and made TextLayout draw underlining where the font asks for it

tags/2021-05-28
jules 8 years ago
parent
commit
89ad7eb0a4
3 changed files with 79 additions and 80 deletions
  1. +15
    -16
      modules/juce_graphics/fonts/juce_GlyphArrangement.cpp
  2. +63
    -63
      modules/juce_graphics/fonts/juce_TextLayout.cpp
  3. +1
    -1
      modules/juce_graphics/fonts/juce_TextLayout.h

+ 15
- 16
modules/juce_graphics/fonts/juce_GlyphArrangement.cpp View File

@@ -96,7 +96,7 @@ void PositionedGlyph::createPath (Path& path) const
{ {
if (! isWhitespace()) if (! isWhitespace())
{ {
if (Typeface* const t = font.getTypeface())
if (auto* t = font.getTypeface())
{ {
Path p; Path p;
t->getOutlineForGlyph (glyph, p); t->getOutlineForGlyph (glyph, p);
@@ -111,7 +111,7 @@ bool PositionedGlyph::hitTest (float px, float py) const
{ {
if (getBounds().contains (px, py) && ! isWhitespace()) if (getBounds().contains (px, py) && ! isWhitespace())
{ {
if (Typeface* const t = font.getTypeface())
if (auto* t = font.getTypeface())
{ {
Path p; Path p;
t->getOutlineForGlyph (glyph, p); t->getOutlineForGlyph (glyph, p);
@@ -207,7 +207,7 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font,
const int textLen = newGlyphs.size(); const int textLen = newGlyphs.size();
glyphs.ensureStorageAllocated (glyphs.size() + textLen); glyphs.ensureStorageAllocated (glyphs.size() + textLen);
String::CharPointerType t (text.getCharPointer());
auto t = text.getCharPointer();
for (int i = 0; i < textLen; ++i) for (int i = 0; i < textLen; ++i)
{ {
@@ -249,7 +249,7 @@ int GlyphArrangement::insertEllipsis (const Font& font, const float maxXPos,
while (endIndex > startIndex) while (endIndex > startIndex)
{ {
const PositionedGlyph& pg = glyphs.getReference (--endIndex);
auto& pg = glyphs.getReference (--endIndex);
xOffset = pg.x; xOffset = pg.x;
yOffset = pg.y; yOffset = pg.y;
@@ -299,7 +299,7 @@ void GlyphArrangement::addJustifiedText (const Font& font,
while (i < glyphs.size()) while (i < glyphs.size())
{ {
const PositionedGlyph& pg = glyphs.getReference (i);
auto& pg = glyphs.getReference (i);
const juce_wchar c = pg.getCharacter(); const juce_wchar c = pg.getCharacter();
if (c == '\r' || c == '\n') if (c == '\r' || c == '\n')
@@ -433,7 +433,7 @@ void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f
GlyphArrangement ga; GlyphArrangement ga;
ga.addJustifiedText (f, text, x, y, width, layout); ga.addJustifiedText (f, text, x, y, width, layout);
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));
auto bb = ga.getBoundingBox (0, -1, false);
float dy = y - bb.getY(); float dy = y - bb.getY();
@@ -485,7 +485,7 @@ void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num,
while (--num >= 0) while (--num >= 0)
{ {
PositionedGlyph& pg = glyphs.getReference (startIndex++);
auto& pg = glyphs.getReference (startIndex++);
pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor;
pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor); pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor);
@@ -505,7 +505,7 @@ Rectangle<float> GlyphArrangement::getBoundingBox (int startIndex, int num, cons
while (--num >= 0) while (--num >= 0)
{ {
const PositionedGlyph& pg = glyphs.getReference (startIndex++);
auto& pg = glyphs.getReference (startIndex++);
if (includeWhitespace || ! pg.isWhitespace()) if (includeWhitespace || ! pg.isWhitespace())
result = result.getUnion (pg.getBounds()); result = result.getUnion (pg.getBounds());
@@ -522,8 +522,8 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, const int num,
if (glyphs.size() > 0 && num > 0) if (glyphs.size() > 0 && num > 0)
{ {
const Rectangle<float> bb (getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified
| Justification::horizontallyCentred)));
auto bb = getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified
| Justification::horizontallyCentred));
float deltaX = 0.0f, deltaY = 0.0f; float deltaX = 0.0f, deltaY = 0.0f;
if (justification.testFlags (Justification::horizontallyJustified)) deltaX = x - bb.getX(); if (justification.testFlags (Justification::horizontallyJustified)) deltaX = x - bb.getX();
@@ -591,8 +591,7 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa
const float startX = glyphs.getReference (start).getLeft(); const float startX = glyphs.getReference (start).getLeft();
const float endX = glyphs.getReference (start + num - 1 - spacesAtEnd).getRight(); const float endX = glyphs.getReference (start + num - 1 - spacesAtEnd).getRight();
const float extraPaddingBetweenWords
= (targetWidth - (endX - startX)) / (float) numSpaces;
const float extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces;
float deltaX = 0.0f; float deltaX = 0.0f;
@@ -764,13 +763,13 @@ void GlyphArrangement::draw (const Graphics& g) const
void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const
{ {
LowLevelGraphicsContext& context = g.getInternalContext();
auto& context = g.getInternalContext();
Font lastFont (context.getFont()); Font lastFont (context.getFont());
bool needToRestore = false; bool needToRestore = false;
for (int i = 0; i < glyphs.size(); ++i) for (int i = 0; i < glyphs.size(); ++i)
{ {
const PositionedGlyph& pg = glyphs.getReference(i);
auto& pg = glyphs.getReference(i);
if (pg.font.isUnderlined()) if (pg.font.isUnderlined())
drawGlyphUnderline (g, pg, i, transform); drawGlyphUnderline (g, pg, i, transform);
@@ -800,8 +799,8 @@ void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform
void GlyphArrangement::createPath (Path& path) const void GlyphArrangement::createPath (Path& path) const
{ {
for (int i = 0; i < glyphs.size(); ++i)
glyphs.getReference (i).createPath (path);
for (auto& g : glyphs)
g.createPath (path);
} }
int GlyphArrangement::findGlyphIndexAt (const float x, const float y) const int GlyphArrangement::findGlyphIndexAt (const float x, const float y) const


+ 63
- 63
modules/juce_graphics/fonts/juce_TextLayout.cpp View File

@@ -48,7 +48,7 @@ TextLayout::Run::Run() noexcept
{ {
} }
TextLayout::Run::Run (Range<int> range, const int numGlyphsToPreallocate)
TextLayout::Run::Run (Range<int> range, int numGlyphsToPreallocate)
: colour (0xff000000), stringRange (range) : colour (0xff000000), stringRange (range)
{ {
glyphs.ensureStorageAllocated (numGlyphsToPreallocate); glyphs.ensureStorageAllocated (numGlyphsToPreallocate);
@@ -94,31 +94,20 @@ Range<float> TextLayout::Line::getLineBoundsX() const noexcept
Range<float> range; Range<float> range;
bool isFirst = true; bool isFirst = true;
for (int i = runs.size(); --i >= 0;)
for (auto* run : runs)
{ {
const Run& run = *runs.getUnchecked(i);
if (run.glyphs.size() > 0)
for (auto& glyph : run->glyphs)
{ {
float minX = run.glyphs.getReference(0).anchor.x;
float maxX = minX;
for (int j = run.glyphs.size(); --j >= 0;)
{
const Glyph& glyph = run.glyphs.getReference (j);
const float x = glyph.anchor.x;
minX = jmin (minX, x);
maxX = jmax (maxX, x + glyph.width);
}
Range<float> runRange (glyph.anchor.x, glyph.anchor.x + glyph.width);
if (isFirst) if (isFirst)
{ {
isFirst = false; isFirst = false;
range = Range<float> (minX, maxX);
range = runRange;
} }
else else
{ {
range = range.getUnionWith (Range<float> (minX, maxX));
range = range.getUnionWith (runRange);
} }
} }
} }
@@ -134,10 +123,10 @@ Range<float> TextLayout::Line::getLineBoundsY() const noexcept
Rectangle<float> TextLayout::Line::getLineBounds() const noexcept Rectangle<float> TextLayout::Line::getLineBounds() const noexcept
{ {
const Range<float> x (getLineBoundsX()),
y (getLineBoundsY());
auto x = getLineBoundsX();
auto y = getLineBoundsY();
return Rectangle<float> (x.getStart(), y.getStart(), x.getLength(), y.getLength());
return { x.getStart(), y.getStart(), x.getLength(), y.getLength() };
} }
//============================================================================== //==============================================================================
@@ -198,28 +187,40 @@ void TextLayout::addLine (Line* line)
lines.add (line); lines.add (line);
} }
void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
void TextLayout::draw (Graphics& g, Rectangle<float> area) const
{ {
const Point<float> origin (justification.appliedToRectangle (Rectangle<float> (width, getHeight()), area).getPosition());
auto origin = justification.appliedToRectangle (Rectangle<float> (width, getHeight()), area).getPosition();
LowLevelGraphicsContext& context = g.getInternalContext();
auto& context = g.getInternalContext();
for (int i = 0; i < lines.size(); ++i)
for (auto* line : lines)
{ {
const Line& line = getLine (i);
const Point<float> lineOrigin (origin + line.lineOrigin);
auto lineOrigin = origin + line->lineOrigin;
for (int j = 0; j < line.runs.size(); ++j)
for (auto* run : line->runs)
{ {
const Run& run = *line.runs.getUnchecked (j);
context.setFont (run.font);
context.setFill (run.colour);
context.setFont (run->font);
context.setFill (run->colour);
for (int k = 0; k < run.glyphs.size(); ++k)
{
const Glyph& glyph = run.glyphs.getReference (k);
for (auto& glyph : run->glyphs)
context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x, context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x,
lineOrigin.y + glyph.anchor.y)); lineOrigin.y + glyph.anchor.y));
if (run->font.isUnderlined())
{
Range<float> runExtent;
for (auto& glyph : run->glyphs)
{
Range<float> glyphRange (glyph.anchor.x, glyph.anchor.x + glyph.width);
runExtent = runExtent.isEmpty() ? glyphRange
: runExtent.getUnionWith (glyphRange);
}
const float lineThickness = run->font.getDescent() * 0.3f;
context.fillRect ({ runExtent.getStart() + lineOrigin.x, lineOrigin.y + lineThickness * 2.0f,
runExtent.getLength(), lineThickness });
} }
} }
} }
@@ -287,7 +288,7 @@ namespace TextLayoutHelpers
{ {
struct Token struct Token
{ {
Token (const String& t, const Font& f, Colour c, const bool whitespace)
Token (const String& t, const Font& f, Colour c, bool whitespace)
: text (t), font (f), colour (c), : text (t), font (f), colour (c),
area (font.getStringWidthFloat (t), f.getHeight()), area (font.getStringWidthFloat (t), f.getHeight()),
isWhitespace (whitespace), isWhitespace (whitespace),
@@ -308,7 +309,7 @@ namespace TextLayoutHelpers
struct TokenList struct TokenList
{ {
TokenList() noexcept : totalLines (0) {}
TokenList() noexcept {}
void createLayout (const AttributedString& text, TextLayout& layout) void createLayout (const AttributedString& text, TextLayout& layout)
{ {
@@ -328,7 +329,7 @@ namespace TextLayoutHelpers
for (int i = 0; i < tokens.size(); ++i) for (int i = 0; i < tokens.size(); ++i)
{ {
const Token& t = *tokens.getUnchecked (i);
auto& t = *tokens.getUnchecked (i);
Array<int> newGlyphs; Array<int> newGlyphs;
Array<float> xOffsets; Array<float> xOffsets;
@@ -364,19 +365,7 @@ namespace TextLayoutHelpers
if (t.isWhitespace || t.isNewLine) if (t.isWhitespace || t.isNewLine)
++charPosition; ++charPosition;
const Token* const nextToken = tokens [i + 1];
if (nextToken == nullptr) // this is the last token
{
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
currentLine->stringRange = Range<int> (lineStartPosition, charPosition);
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
needToSetLineOrigin = true;
}
else
if (auto* nextToken = tokens [i + 1])
{ {
if (t.font != nextToken->font || t.colour != nextToken->colour) if (t.font != nextToken->font || t.colour != nextToken->colour)
{ {
@@ -400,6 +389,16 @@ namespace TextLayoutHelpers
needToSetLineOrigin = true; needToSetLineOrigin = true;
} }
} }
else
{
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
currentLine->stringRange = Range<int> (lineStartPosition, charPosition);
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
needToSetLineOrigin = true;
}
} }
if ((text.getJustification().getFlags() & (Justification::right | Justification::horizontallyCentred)) != 0) if ((text.getJustification().getFlags() & (Justification::right | Justification::horizontallyCentred)) != 0)
@@ -423,7 +422,7 @@ namespace TextLayoutHelpers
static void addRun (TextLayout::Line& glyphLine, TextLayout::Run* glyphRun, static void addRun (TextLayout::Line& glyphLine, TextLayout::Run* glyphRun,
const Token& t, const int start, const int end) const Token& t, const int start, const int end)
{ {
glyphRun->stringRange = Range<int> (start, end);
glyphRun->stringRange = { start, end };
glyphRun->font = t.font; glyphRun->font = t.font;
glyphRun->colour = t.colour; glyphRun->colour = t.colour;
glyphLine.ascent = jmax (glyphLine.ascent, t.font.getAscent()); glyphLine.ascent = jmax (glyphLine.ascent, t.font.getAscent());
@@ -441,7 +440,7 @@ namespace TextLayoutHelpers
void appendText (const String& stringText, const Font& font, Colour colour) void appendText (const String& stringText, const Font& font, Colour colour)
{ {
String::CharPointerType t (stringText.getCharPointer());
auto t = stringText.getCharPointer();
String currentString; String currentString;
int lastCharType = 0; int lastCharType = 0;
@@ -483,13 +482,13 @@ namespace TextLayoutHelpers
for (i = 0; i < tokens.size(); ++i) for (i = 0; i < tokens.size(); ++i)
{ {
Token& t = *tokens.getUnchecked(i);
auto& t = *tokens.getUnchecked(i);
t.area.setPosition (x, y); t.area.setPosition (x, y);
t.line = totalLines; t.line = totalLines;
x += t.area.getWidth(); x += t.area.getWidth();
h = jmax (h, t.area.getHeight() + extraLineSpacing); h = jmax (h, t.area.getHeight() + extraLineSpacing);
const Token* const nextTok = tokens[i + 1];
auto* nextTok = tokens[i + 1];
if (nextTok == nullptr) if (nextTok == nullptr)
break; break;
@@ -514,7 +513,7 @@ namespace TextLayoutHelpers
{ {
while (--i >= 0) while (--i >= 0)
{ {
Token& tok = *tokens.getUnchecked (i);
auto& tok = *tokens.getUnchecked (i);
if (tok.line == totalLines) if (tok.line == totalLines)
tok.lineHeight = height; tok.lineHeight = height;
@@ -530,7 +529,7 @@ namespace TextLayoutHelpers
for (int i = 0; i < numAttributes; ++i) for (int i = 0; i < numAttributes; ++i)
{ {
const AttributedString::Attribute& attr = text.getAttribute (i);
auto& attr = text.getAttribute (i);
appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()), appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()),
attr.font, attr.colour); attr.font, attr.colour);
@@ -539,7 +538,8 @@ namespace TextLayoutHelpers
static String getTrimmedEndIfNotAllWhitespace (const String& s) static String getTrimmedEndIfNotAllWhitespace (const String& s)
{ {
String trimmed (s.trimEnd());
auto trimmed = s.trimEnd();
if (trimmed.isEmpty() && s.isNotEmpty()) if (trimmed.isEmpty() && s.isNotEmpty())
trimmed = s.replaceCharacters ("\r\n\t", " "); trimmed = s.replaceCharacters ("\r\n\t", " ");
@@ -547,7 +547,7 @@ namespace TextLayoutHelpers
} }
OwnedArray<Token> tokens; OwnedArray<Token> tokens;
int totalLines;
int totalLines = 0;
JUCE_DECLARE_NON_COPYABLE (TokenList) JUCE_DECLARE_NON_COPYABLE (TokenList)
}; };
@@ -562,15 +562,15 @@ void TextLayout::createStandardLayout (const AttributedString& text)
void TextLayout::recalculateSize() void TextLayout::recalculateSize()
{ {
if (lines.size() > 0)
if (! lines.isEmpty())
{ {
Rectangle<float> bounds (lines.getFirst()->getLineBounds());
auto bounds = lines.getFirst()->getLineBounds();
for (int i = lines.size(); --i > 0;)
bounds = bounds.getUnion (lines.getUnchecked(i)->getLineBounds());
for (auto* line : lines)
bounds = bounds.getUnion (line->getLineBounds());
for (int i = lines.size(); --i >= 0;)
lines.getUnchecked(i)->lineOrigin.x -= bounds.getX();
for (auto* line : lines)
line->lineOrigin.x -= bounds.getX();
width = bounds.getWidth(); width = bounds.getWidth();
height = bounds.getHeight(); height = bounds.getHeight();


+ 1
- 1
modules/juce_graphics/fonts/juce_TextLayout.h View File

@@ -81,7 +81,7 @@ public:
The position of the text within the rectangle is controlled by the justification The position of the text within the rectangle is controlled by the justification
flags set in the original AttributedString that was used to create this layout. flags set in the original AttributedString that was used to create this layout.
*/ */
void draw (Graphics&, const Rectangle<float>& area) const;
void draw (Graphics&, Rectangle<float> area) const;
//============================================================================== //==============================================================================
/** A positioned glyph. */ /** A positioned glyph. */


Loading…
Cancel
Save