mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
A bit of refactoring inside GlyphArrangement, to avoid som edge-cases where the wrong number of lines is chosen.
This commit is contained in:
parent
171ef8dd4b
commit
02460bb95b
3 changed files with 208 additions and 161 deletions
|
|
@ -278,13 +278,13 @@ Font& Font::operator= (const Font& other) noexcept
|
|||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
Font::Font (Font&& other) noexcept
|
||||
: font (static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font))
|
||||
: font (static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font))
|
||||
{
|
||||
}
|
||||
|
||||
Font& Font::operator= (Font&& other) noexcept
|
||||
{
|
||||
font = static_cast <ReferenceCountedObjectPtr <SharedFontInternal>&&> (other.font);
|
||||
font = static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -640,7 +640,7 @@ float Font::getStringWidthFloat (const String& text) const
|
|||
return w * font->height * font->horizontalScale;
|
||||
}
|
||||
|
||||
void Font::getGlyphPositions (const String& text, Array <int>& glyphs, Array <float>& xOffsets) const
|
||||
void Font::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const
|
||||
{
|
||||
getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,27 @@ PositionedGlyph::PositionedGlyph (const PositionedGlyph& other)
|
|||
{
|
||||
}
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
PositionedGlyph::PositionedGlyph (PositionedGlyph&& other) noexcept
|
||||
: font (static_cast<Font&&> (other.font)),
|
||||
character (other.character), glyph (other.glyph),
|
||||
x (other.x), y (other.y), w (other.w), whitespace (other.whitespace)
|
||||
{
|
||||
}
|
||||
|
||||
PositionedGlyph& PositionedGlyph::operator= (PositionedGlyph&& other) noexcept
|
||||
{
|
||||
font = static_cast<Font&&> (other.font);
|
||||
character = other.character;
|
||||
glyph = other.glyph;
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
w = other.w;
|
||||
whitespace = other.whitespace;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
PositionedGlyph::~PositionedGlyph() {}
|
||||
|
||||
PositionedGlyph& PositionedGlyph::operator= (const PositionedGlyph& other)
|
||||
|
|
@ -182,8 +203,8 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font,
|
|||
{
|
||||
if (text.isNotEmpty())
|
||||
{
|
||||
Array <int> newGlyphs;
|
||||
Array <float> xOffsets;
|
||||
Array<int> newGlyphs;
|
||||
Array<float> xOffsets;
|
||||
font.getGlyphPositions (text, newGlyphs, xOffsets);
|
||||
const int textLen = newGlyphs.size();
|
||||
glyphs.ensureStorageAllocated (glyphs.size() + textLen);
|
||||
|
|
@ -353,174 +374,40 @@ void GlyphArrangement::addFittedText (const Font& f,
|
|||
|
||||
if (text.containsAnyOf ("\r\n"))
|
||||
{
|
||||
GlyphArrangement ga;
|
||||
ga.addJustifiedText (f, text, x, y, width, layout);
|
||||
|
||||
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));
|
||||
|
||||
float dy = y - bb.getY();
|
||||
|
||||
if (layout.testFlags (Justification::verticallyCentred)) dy += (height - bb.getHeight()) * 0.5f;
|
||||
else if (layout.testFlags (Justification::bottom)) dy += (height - bb.getHeight());
|
||||
|
||||
ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);
|
||||
|
||||
glyphs.addArray (ga.glyphs);
|
||||
return;
|
||||
addLinesWithLineBreaks (text, f, x, y, width, height, layout);
|
||||
}
|
||||
|
||||
int startIndex = glyphs.size();
|
||||
addLineOfText (f, text.trim(), x, y);
|
||||
|
||||
if (glyphs.size() > startIndex)
|
||||
else
|
||||
{
|
||||
float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
|
||||
- glyphs.getReference (startIndex).getLeft();
|
||||
const int startIndex = glyphs.size();
|
||||
const String trimmed (text.trim());
|
||||
addLineOfText (f, trimmed, x, y);
|
||||
const int numGlyphs = glyphs.size() - startIndex;
|
||||
|
||||
if (lineWidth <= 0)
|
||||
return;
|
||||
|
||||
if (lineWidth * minimumHorizontalScale < width)
|
||||
if (numGlyphs > 0)
|
||||
{
|
||||
if (lineWidth > width)
|
||||
stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex,
|
||||
width / lineWidth);
|
||||
const float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
|
||||
- glyphs.getReference (startIndex).getLeft();
|
||||
|
||||
justifyGlyphs (startIndex, glyphs.size() - startIndex,
|
||||
x, y, width, height, layout);
|
||||
}
|
||||
else if (maximumLines <= 1)
|
||||
{
|
||||
fitLineIntoSpace (startIndex, glyphs.size() - startIndex,
|
||||
x, y, width, height, f, layout, minimumHorizontalScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
Font font (f);
|
||||
String txt (text.trim());
|
||||
const int length = txt.length();
|
||||
const int originalStartIndex = startIndex;
|
||||
int numLines = 1;
|
||||
|
||||
if (length <= 12 && ! txt.containsAnyOf (" -\t\r\n"))
|
||||
maximumLines = 1;
|
||||
|
||||
maximumLines = jmin (maximumLines, length);
|
||||
|
||||
while (numLines < maximumLines)
|
||||
if (lineWidth > 0)
|
||||
{
|
||||
++numLines;
|
||||
|
||||
const float newFontHeight = height / (float) numLines;
|
||||
|
||||
if (newFontHeight < font.getHeight())
|
||||
if (lineWidth * minimumHorizontalScale < width)
|
||||
{
|
||||
font.setHeight (jmax (8.0f, newFontHeight));
|
||||
if (lineWidth > width)
|
||||
stretchRangeOfGlyphs (startIndex, numGlyphs, width / lineWidth);
|
||||
|
||||
removeRangeOfGlyphs (startIndex, -1);
|
||||
addLineOfText (font, txt, x, y);
|
||||
|
||||
lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
|
||||
- glyphs.getReference (startIndex).getLeft();
|
||||
justifyGlyphs (startIndex, numGlyphs, x, y, width, height, layout);
|
||||
}
|
||||
|
||||
if (numLines > lineWidth / width || newFontHeight < 8.0f)
|
||||
break;
|
||||
}
|
||||
|
||||
if (numLines < 1)
|
||||
numLines = 1;
|
||||
|
||||
float lineY = y;
|
||||
float widthPerLine = lineWidth / numLines;
|
||||
|
||||
for (int line = 0; line < numLines; ++line)
|
||||
{
|
||||
int i = startIndex;
|
||||
float lineStartX = glyphs.getReference (startIndex).getLeft();
|
||||
|
||||
if (line == numLines - 1)
|
||||
else if (maximumLines <= 1)
|
||||
{
|
||||
widthPerLine = width;
|
||||
i = glyphs.size();
|
||||
fitLineIntoSpace (startIndex, numGlyphs, x, y, width, height,
|
||||
f, layout, minimumHorizontalScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i < glyphs.size())
|
||||
{
|
||||
lineWidth = (glyphs.getReference (i).getRight() - lineStartX);
|
||||
|
||||
if (lineWidth > widthPerLine)
|
||||
{
|
||||
// got to a point where the line's too long, so skip forward to find a
|
||||
// good place to break it..
|
||||
const int searchStartIndex = i;
|
||||
|
||||
while (i < glyphs.size())
|
||||
{
|
||||
if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width)
|
||||
{
|
||||
if (glyphs.getReference (i).isWhitespace()
|
||||
|| glyphs.getReference (i).getCharacter() == '-')
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't find a suitable break, so try looking backwards..
|
||||
i = searchStartIndex;
|
||||
|
||||
for (int back = 1; back < jmin (7, i - startIndex - 1); ++back)
|
||||
{
|
||||
if (glyphs.getReference (i - back).isWhitespace()
|
||||
|| glyphs.getReference (i - back).getCharacter() == '-')
|
||||
{
|
||||
i -= back - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
int wsStart = i;
|
||||
while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace())
|
||||
--wsStart;
|
||||
|
||||
int wsEnd = i;
|
||||
|
||||
while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace())
|
||||
++wsEnd;
|
||||
|
||||
removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
|
||||
i = jmax (wsStart, startIndex + 1);
|
||||
splitLines (trimmed, f, startIndex, x, y, width, height,
|
||||
maximumLines, lineWidth, layout, minimumHorizontalScale);
|
||||
}
|
||||
|
||||
i -= fitLineIntoSpace (startIndex, i - startIndex,
|
||||
x, lineY, width, font.getHeight(), font,
|
||||
layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
|
||||
minimumHorizontalScale);
|
||||
|
||||
startIndex = i;
|
||||
lineY += font.getHeight();
|
||||
|
||||
if (startIndex >= glyphs.size())
|
||||
break;
|
||||
}
|
||||
|
||||
justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
|
||||
x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -540,6 +427,24 @@ void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float d
|
|||
}
|
||||
}
|
||||
|
||||
void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f,
|
||||
int x, int y, int width, int height, Justification layout)
|
||||
{
|
||||
GlyphArrangement ga;
|
||||
ga.addJustifiedText (f, text, x, y, width, layout);
|
||||
|
||||
const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));
|
||||
|
||||
float dy = y - bb.getY();
|
||||
|
||||
if (layout.testFlags (Justification::verticallyCentred)) dy += (height - bb.getHeight()) * 0.5f;
|
||||
else if (layout.testFlags (Justification::bottom)) dy += (height - bb.getHeight());
|
||||
|
||||
ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);
|
||||
|
||||
glyphs.addArray (ga.glyphs);
|
||||
}
|
||||
|
||||
int GlyphArrangement::fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font& font,
|
||||
Justification justification, float minimumHorizontalScale)
|
||||
{
|
||||
|
|
@ -702,9 +607,142 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void GlyphArrangement::splitLines (const String& text, Font font, int startIndex,
|
||||
int x, int y, int width, int height, int maximumLines,
|
||||
float lineWidth, Justification layout, float minimumHorizontalScale)
|
||||
{
|
||||
const int length = text.length();
|
||||
const int originalStartIndex = startIndex;
|
||||
int numLines = 1;
|
||||
|
||||
if (length <= 12 && ! text.containsAnyOf (" -\t\r\n"))
|
||||
maximumLines = 1;
|
||||
|
||||
maximumLines = jmin (maximumLines, length);
|
||||
|
||||
while (numLines < maximumLines)
|
||||
{
|
||||
++numLines;
|
||||
|
||||
const float newFontHeight = height / (float) numLines;
|
||||
|
||||
if (newFontHeight < font.getHeight())
|
||||
{
|
||||
font.setHeight (jmax (8.0f, newFontHeight));
|
||||
|
||||
removeRangeOfGlyphs (startIndex, -1);
|
||||
addLineOfText (font, text, x, y);
|
||||
|
||||
lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
|
||||
- glyphs.getReference (startIndex).getLeft();
|
||||
}
|
||||
|
||||
// Try to estimate the point at which there are enough lines to fit the text,
|
||||
// allowing for unevenness in the lengths due to differently sized words.
|
||||
const float lineLengthUnevennessAllowance = 80.0f;
|
||||
|
||||
if (numLines > (lineWidth + lineLengthUnevennessAllowance) / width || newFontHeight < 8.0f)
|
||||
break;
|
||||
}
|
||||
|
||||
if (numLines < 1)
|
||||
numLines = 1;
|
||||
|
||||
float lineY = y;
|
||||
float widthPerLine = lineWidth / numLines;
|
||||
|
||||
for (int line = 0; line < numLines; ++line)
|
||||
{
|
||||
int i = startIndex;
|
||||
float lineStartX = glyphs.getReference (startIndex).getLeft();
|
||||
|
||||
if (line == numLines - 1)
|
||||
{
|
||||
widthPerLine = width;
|
||||
i = glyphs.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i < glyphs.size())
|
||||
{
|
||||
lineWidth = (glyphs.getReference (i).getRight() - lineStartX);
|
||||
|
||||
if (lineWidth > widthPerLine)
|
||||
{
|
||||
// got to a point where the line's too long, so skip forward to find a
|
||||
// good place to break it..
|
||||
const int searchStartIndex = i;
|
||||
|
||||
while (i < glyphs.size())
|
||||
{
|
||||
if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width)
|
||||
{
|
||||
if (glyphs.getReference (i).isWhitespace()
|
||||
|| glyphs.getReference (i).getCharacter() == '-')
|
||||
{
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't find a suitable break, so try looking backwards..
|
||||
i = searchStartIndex;
|
||||
|
||||
for (int back = 1; back < jmin (7, i - startIndex - 1); ++back)
|
||||
{
|
||||
if (glyphs.getReference (i - back).isWhitespace()
|
||||
|| glyphs.getReference (i - back).getCharacter() == '-')
|
||||
{
|
||||
i -= back - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
int wsStart = i;
|
||||
while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace())
|
||||
--wsStart;
|
||||
|
||||
int wsEnd = i;
|
||||
while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace())
|
||||
++wsEnd;
|
||||
|
||||
removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
|
||||
i = jmax (wsStart, startIndex + 1);
|
||||
}
|
||||
|
||||
i -= fitLineIntoSpace (startIndex, i - startIndex,
|
||||
x, lineY, width, font.getHeight(), font,
|
||||
layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
|
||||
minimumHorizontalScale);
|
||||
|
||||
startIndex = i;
|
||||
lineY += font.getHeight();
|
||||
|
||||
if (startIndex >= glyphs.size())
|
||||
break;
|
||||
}
|
||||
|
||||
justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
|
||||
x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
inline void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg,
|
||||
const int i, const AffineTransform& transform) const
|
||||
void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg,
|
||||
const int i, const AffineTransform& transform) const
|
||||
{
|
||||
const float lineThickness = (pg.font.getDescent()) * 0.3f;
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ public:
|
|||
|
||||
PositionedGlyph (const PositionedGlyph&);
|
||||
PositionedGlyph& operator= (const PositionedGlyph&);
|
||||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
PositionedGlyph (PositionedGlyph&&) noexcept;
|
||||
PositionedGlyph& operator= (PositionedGlyph&&) noexcept;
|
||||
#endif
|
||||
|
||||
~PositionedGlyph();
|
||||
|
||||
/** Returns the character the glyph represents. */
|
||||
|
|
@ -295,12 +301,15 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
Array <PositionedGlyph> glyphs;
|
||||
Array<PositionedGlyph> glyphs;
|
||||
|
||||
int insertEllipsis (const Font&, float maxXPos, int startIndex, int endIndex);
|
||||
int fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font&,
|
||||
Justification, float minimumHorizontalScale);
|
||||
void spreadOutLine (int start, int numGlyphs, float targetWidth);
|
||||
void splitLines (const String&, Font, int start, int x, int y, int w, int h, int maxLines,
|
||||
float lineWidth, Justification, float minimumHorizontalScale);
|
||||
void addLinesWithLineBreaks (const String&, const Font&, int x, int y, int width, int height, Justification);
|
||||
void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, const AffineTransform&) const;
|
||||
|
||||
JUCE_LEAK_DETECTOR (GlyphArrangement)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue