From a49baa3e52bcbd14b1b0c9dc5a554514d83a4e73 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 29 Dec 2014 12:07:31 +0000 Subject: [PATCH] Modified TextLayout creation functions to take an optional maximum height as well as a maximum width. --- .../juce_graphics/fonts/juce_TextLayout.cpp | 148 ++++++++++-------- modules/juce_graphics/fonts/juce_TextLayout.h | 35 ++++- .../juce_win32_DirectWriteTypeLayout.cpp | 13 +- 3 files changed, 121 insertions(+), 75 deletions(-) diff --git a/modules/juce_graphics/fonts/juce_TextLayout.cpp b/modules/juce_graphics/fonts/juce_TextLayout.cpp index 526cac1a9b..1989bc7b7b 100644 --- a/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -22,8 +22,8 @@ ============================================================================== */ -TextLayout::Glyph::Glyph (const int glyphCode_, Point anchor_, float width_) noexcept - : glyphCode (glyphCode_), anchor (anchor_), width (width_) +TextLayout::Glyph::Glyph (const int glyph, Point anch, float w) noexcept + : glyphCode (glyph), anchor (anch), width (w) { } @@ -70,11 +70,10 @@ TextLayout::Line::Line() noexcept { } -TextLayout::Line::Line (Range stringRange_, Point 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 range, Point 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 TextLayout::Line::getLineBoundsX() const noexcept return range + lineOrigin.x; } +Range TextLayout::Line::getLineBoundsY() const noexcept +{ + return Range (lineOrigin.y - ascent, + lineOrigin.y + descent); +} + +Rectangle TextLayout::Line::getLineBounds() const noexcept +{ + const Range x (getLineBoundsX()), + y (getLineBoundsY()); + + return Rectangle (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 &&> (other.lines)), - width (other.width), + : lines (static_cast&&> (other.lines)), + width (other.width), height (other.height), justification (other.justification) { } TextLayout& TextLayout::operator= (TextLayout&& other) noexcept { - lines = static_cast &&> (other.lines); + lines = static_cast&&> (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& 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 lineOrigin (origin + line.lineOrigin); @@ -221,15 +228,60 @@ void TextLayout::draw (Graphics& g, const Rectangle& 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 newGlyphs; - Array xOffsets; + Array newGlyphs; + Array 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 range (lines.getFirst()->getLineBoundsX()); + Rectangle 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(); } } diff --git a/modules/juce_graphics/fonts/juce_TextLayout.h b/modules/juce_graphics/fonts/juce_TextLayout.h index b5f9eeadd1..0ae8237d99 100644 --- a/modules/juce_graphics/fonts/juce_TextLayout.h +++ b/modules/juce_graphics/fonts/juce_TextLayout.h @@ -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& area) const; + void draw (Graphics&, const Rectangle& area) const; //============================================================================== /** A positioned glyph. */ @@ -131,6 +144,12 @@ public: /** Returns the X position range which contains all the glyphs in this line. */ Range getLineBoundsX() const noexcept; + /** Returns the Y position range which contains all the glyphs in this line. */ + Range getLineBoundsY() const noexcept; + + /** Returns the smallest rectangle which contains all the glyphs in this line. */ + Rectangle getLineBounds() const noexcept; + OwnedArray runs; /**< The glyph-runs in this line. */ Range 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 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) }; diff --git a/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index 83e3c9d94f..fd2c7a7033 100644 --- a/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -311,6 +311,13 @@ namespace DirectWriteTypeLayout setTextFormatProperties (text, dwTextFormat); + { + DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 }; + ComSmartPtr 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 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 dwTextLayout; - if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout)) + if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, + directWriteFactory, fontCollection, dwTextLayout)) { ComSmartPtr d2dBrush; renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)),