1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Modified TextLayout creation functions to take an optional maximum height as well as a maximum width.

This commit is contained in:
jules 2014-12-29 12:07:31 +00:00
parent 04cb9bf3e8
commit a49baa3e52
3 changed files with 121 additions and 75 deletions

View file

@ -22,8 +22,8 @@
==============================================================================
*/
TextLayout::Glyph::Glyph (const int glyphCode_, Point<float> anchor_, float width_) noexcept
: glyphCode (glyphCode_), anchor (anchor_), width (width_)
TextLayout::Glyph::Glyph (const int glyph, Point<float> anch, float w) noexcept
: glyphCode (glyph), anchor (anch), width (w)
{
}
@ -70,11 +70,10 @@ TextLayout::Line::Line() noexcept
{
}
TextLayout::Line::Line (Range<int> stringRange_, Point<float> lineOrigin_,
const float ascent_, const float descent_, const float leading_,
const int numRunsToPreallocate)
: stringRange (stringRange_), lineOrigin (lineOrigin_),
ascent (ascent_), descent (descent_), leading (leading_)
TextLayout::Line::Line (Range<int> range, Point<float> o, float asc, float desc,
float lead, int numRunsToPreallocate)
: stringRange (range), lineOrigin (o),
ascent (asc), descent (desc), leading (lead)
{
runs.ensureStorageAllocated (numRunsToPreallocate);
}
@ -127,14 +126,28 @@ Range<float> TextLayout::Line::getLineBoundsX() const noexcept
return range + lineOrigin.x;
}
Range<float> TextLayout::Line::getLineBoundsY() const noexcept
{
return Range<float> (lineOrigin.y - ascent,
lineOrigin.y + descent);
}
Rectangle<float> TextLayout::Line::getLineBounds() const noexcept
{
const Range<float> x (getLineBoundsX()),
y (getLineBoundsY());
return Rectangle<float> (x.getStart(), y.getStart(), x.getLength(), y.getLength());
}
//==============================================================================
TextLayout::TextLayout()
: width (0), justification (Justification::topLeft)
: width (0), height (0), justification (Justification::topLeft)
{
}
TextLayout::TextLayout (const TextLayout& other)
: width (other.width),
: width (other.width), height (other.height),
justification (other.justification)
{
lines.addCopiesOf (other.lines);
@ -142,16 +155,17 @@ TextLayout::TextLayout (const TextLayout& other)
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
TextLayout::TextLayout (TextLayout&& other) noexcept
: lines (static_cast <OwnedArray<Line>&&> (other.lines)),
width (other.width),
: lines (static_cast<OwnedArray<Line>&&> (other.lines)),
width (other.width), height (other.height),
justification (other.justification)
{
}
TextLayout& TextLayout::operator= (TextLayout&& other) noexcept
{
lines = static_cast <OwnedArray<Line>&&> (other.lines);
lines = static_cast<OwnedArray<Line>&&> (other.lines);
width = other.width;
height = other.height;
justification = other.justification;
return *this;
}
@ -160,6 +174,7 @@ TextLayout& TextLayout::operator= (TextLayout&& other) noexcept
TextLayout& TextLayout::operator= (const TextLayout& other)
{
width = other.width;
height = other.height;
justification = other.justification;
lines.clear();
lines.addCopiesOf (other.lines);
@ -170,17 +185,9 @@ TextLayout::~TextLayout()
{
}
float TextLayout::getHeight() const noexcept
{
if (const Line* const lastLine = lines.getLast())
return lastLine->lineOrigin.y + lastLine->descent;
return 0.0f;
}
TextLayout::Line& TextLayout::getLine (const int index) const
{
return *lines[index];
return *lines.getUnchecked (index);
}
void TextLayout::ensureStorageAllocated (int numLinesNeeded)
@ -199,7 +206,7 @@ void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
LowLevelGraphicsContext& context = g.getInternalContext();
for (int i = 0; i < getNumLines(); ++i)
for (int i = 0; i < lines.size(); ++i)
{
const Line& line = getLine (i);
const Point<float> lineOrigin (origin + line.lineOrigin);
@ -221,15 +228,60 @@ void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
}
void TextLayout::createLayout (const AttributedString& text, float maxWidth)
{
createLayout (text, maxWidth, 1.0e7f);
}
void TextLayout::createLayout (const AttributedString& text, float maxWidth, float maxHeight)
{
lines.clear();
width = maxWidth;
height = maxHeight;
justification = text.getJustification();
if (! createNativeLayout (text))
createStandardLayout (text);
recalculateWidth (text);
recalculateSize (text);
}
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth)
{
createLayoutWithBalancedLineLengths (text, maxWidth, 1.0e7f);
}
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth, float maxHeight)
{
const float minimumWidth = maxWidth / 2.0f;
float bestWidth = maxWidth;
float bestLineProportion = 0.0f;
while (maxWidth > minimumWidth)
{
createLayout (text, maxWidth, maxHeight);
if (getNumLines() < 2)
return;
const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
const float shortestLine = jmin (line1, line2);
const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f;
if (prop > 0.9f)
return;
if (prop > bestLineProportion)
{
bestLineProportion = prop;
bestWidth = maxWidth;
}
maxWidth -= 10.0f;
}
if (bestWidth != maxWidth)
createLayout (text, bestWidth, maxHeight);
}
//==============================================================================
@ -305,8 +357,8 @@ namespace TextLayoutHelpers
{
const Token& t = *tokens.getUnchecked (i);
Array <int> newGlyphs;
Array <float> xOffsets;
Array<int> newGlyphs;
Array<float> xOffsets;
t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets);
if (currentRun == nullptr) currentRun = new TextLayout::Run();
@ -561,41 +613,6 @@ namespace TextLayoutHelpers
};
}
//==============================================================================
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth)
{
const float minimumWidth = maxWidth / 2.0f;
float bestWidth = maxWidth;
float bestLineProportion = 0.0f;
while (maxWidth > minimumWidth)
{
createLayout (text, maxWidth);
if (getNumLines() < 2)
return;
const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
const float shortestLine = jmin (line1, line2);
const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f;
if (prop > 0.9f)
return;
if (prop > bestLineProportion)
{
bestLineProportion = prop;
bestWidth = maxWidth;
}
maxWidth -= 10.0f;
}
if (bestWidth != maxWidth)
createLayout (text, bestWidth);
}
//==============================================================================
void TextLayout::createStandardLayout (const AttributedString& text)
{
@ -603,18 +620,19 @@ void TextLayout::createStandardLayout (const AttributedString& text)
l.createLayout (text, *this);
}
void TextLayout::recalculateWidth (const AttributedString& text)
void TextLayout::recalculateSize (const AttributedString& text)
{
if (lines.size() > 0 && text.getReadingDirection() != AttributedString::rightToLeft)
{
Range<float> range (lines.getFirst()->getLineBoundsX());
Rectangle<float> bounds (lines.getFirst()->getLineBounds());
for (int i = lines.size(); --i > 0;)
range = range.getUnionWith (lines.getUnchecked(i)->getLineBoundsX());
bounds = bounds.getUnion (lines.getUnchecked(i)->getLineBounds());
for (int i = lines.size(); --i >= 0;)
lines.getUnchecked(i)->lineOrigin.x -= range.getStart();
lines.getUnchecked(i)->lineOrigin.x -= bounds.getX();
width = range.getLength();
width = bounds.getWidth();
height = bounds.getHeight();
}
}

View file

@ -46,7 +46,7 @@ public:
TextLayout (const TextLayout&);
TextLayout& operator= (const TextLayout&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
TextLayout (TextLayout&& other) noexcept;
TextLayout (TextLayout&&) noexcept;
TextLayout& operator= (TextLayout&&) noexcept;
#endif
@ -57,7 +57,12 @@ public:
/** Creates a layout from the given attributed string.
This will replace any data that is currently stored in the layout.
*/
void createLayout (const AttributedString& text, float maxWidth);
void createLayout (const AttributedString&, float maxWidth);
/** Creates a layout from the given attributed string, given some size constraints.
This will replace any data that is currently stored in the layout.
*/
void createLayout (const AttributedString&, float maxWidth, float maxHeight);
/** Creates a layout, attempting to choose a width which results in lines
of a similar length.
@ -65,13 +70,21 @@ public:
This will be slower than the normal createLayout method, but produces a
tidier result.
*/
void createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth);
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth);
/** Creates a layout, attempting to choose a width which results in lines
of a similar length.
This will be slower than the normal createLayout method, but produces a
tidier result.
*/
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth, float maxHeight);
/** Draws the layout within the specified area.
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.
*/
void draw (Graphics& g, const Rectangle<float>& area) const;
void draw (Graphics&, const Rectangle<float>& area) const;
//==============================================================================
/** A positioned glyph. */
@ -131,6 +144,12 @@ public:
/** Returns the X position range which contains all the glyphs in this line. */
Range<float> getLineBoundsX() const noexcept;
/** Returns the Y position range which contains all the glyphs in this line. */
Range<float> getLineBoundsY() const noexcept;
/** Returns the smallest rectangle which contains all the glyphs in this line. */
Rectangle<float> getLineBounds() const noexcept;
OwnedArray<Run> runs; /**< The glyph-runs in this line. */
Range<int> stringRange; /**< The character range that this line represents in the
original string that was used to create it. */
@ -147,7 +166,7 @@ public:
float getWidth() const noexcept { return width; }
/** Returns the maximum height of the content. */
float getHeight() const noexcept;
float getHeight() const noexcept { return height; }
/** Returns the number of lines in the layout. */
int getNumLines() const noexcept { return lines.size(); }
@ -157,19 +176,19 @@ public:
/** Adds a line to the layout. The layout will take ownership of this line object
and will delete it when it is no longer needed. */
void addLine (Line* line);
void addLine (Line*);
/** Pre-allocates space for the specified number of lines. */
void ensureStorageAllocated (int numLinesNeeded);
private:
OwnedArray<Line> lines;
float width;
float width, height;
Justification justification;
void createStandardLayout (const AttributedString&);
bool createNativeLayout (const AttributedString&);
void recalculateWidth (const AttributedString&);
void recalculateSize (const AttributedString&);
JUCE_LEAK_DETECTOR (TextLayout)
};

View file

@ -311,6 +311,13 @@ namespace DirectWriteTypeLayout
setTextFormatProperties (text, dwTextFormat);
{
DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 };
ComSmartPtr<IDWriteInlineObject> trimmingSign;
hr = directWriteFactory->CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress());
hr = dwTextFormat->SetTrimming (&trimming, trimmingSign);
}
const int textLen = text.getText().length();
hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat,
@ -344,7 +351,8 @@ namespace DirectWriteTypeLayout
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
if (! setupLayout (text, layout.getWidth(), 1.0e7f, renderTarget, directWriteFactory, fontCollection, dwTextLayout))
if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget,
directWriteFactory, fontCollection, dwTextLayout))
return;
UINT32 actualLineCount = 0;
@ -374,7 +382,8 @@ namespace DirectWriteTypeLayout
{
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout))
if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget,
directWriteFactory, fontCollection, dwTextLayout))
{
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush;
renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)),