//------------------------------------------------------------------------------ void MiscPreferences::drawGroupComponentOutline(Graphics& g, int width, int height, const String& text, const Justification& position) { const float textH = 15.0f; const float indent = 3.0f; const float textEdgeGap = 4.0f; float cs = 5.0f; Font f (textH, Font::bold); Path p; float x = indent; float y = f.getAscent() - 3.0f; float w = width - x * 2.0f; float h = height - y - indent; cs = jmin (cs, w * 0.5f, h * 0.5f); const float cs2 = 2.0f * cs; float textW = jlimit (0.0f, f.getStringWidth (text) + textEdgeGap * 2.0f, w - cs2); float textX = cs + textEdgeGap; if (position.testFlags (Justification::verticallyCentred)) textX = cs + (w - cs2 - textW) * 0.5f; else if (position.testFlags (Justification::right)) textX = w - cs - textW - textEdgeGap; p.startNewSubPath (x + textX + textW, y); p.lineTo (x + w - cs, y); p.addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); p.lineTo (x + w, y + h - cs); p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); p.lineTo (x + cs, y + h); p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); p.lineTo (x, y + cs); p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f); p.lineTo (x + textX, y); g.setColour (textColour); g.strokePath (p, PathStrokeType (2.0f)); g.setColour (textColour); g.setFont (f); g.drawText (text, roundFloatToInt (x + textX), 0, roundFloatToInt (textW), roundFloatToInt (textH), Justification::centred, true); }
AffineTransform Path::getTransformToScaleToFit (const float x, const float y, const float w, const float h, const bool preserveProportions, Justification justification) const { Rectangle<float> boundsRect (getBounds()); if (preserveProportions) { if (w <= 0 || h <= 0 || boundsRect.isEmpty()) return AffineTransform::identity; float newW, newH; const float srcRatio = boundsRect.getHeight() / boundsRect.getWidth(); if (srcRatio > h / w) { newW = h / srcRatio; newH = h; } else { newW = w; newH = w * srcRatio; } float newXCentre = x; float newYCentre = y; if (justification.testFlags (Justification::left)) newXCentre += newW * 0.5f; else if (justification.testFlags (Justification::right)) newXCentre += w - newW * 0.5f; else newXCentre += w * 0.5f; if (justification.testFlags (Justification::top)) newYCentre += newH * 0.5f; else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f; else newYCentre += h * 0.5f; return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(), boundsRect.getHeight() * -0.5f - boundsRect.getY()) .scaled (newW / boundsRect.getWidth(), newH / boundsRect.getHeight()) .translated (newXCentre, newYCentre); } else { return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY()) .scaled (w / boundsRect.getWidth(), h / boundsRect.getHeight()) .translated (x, y); } }
void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f, float x, float y, float width, float 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); }
void GlyphArrangement::justifyGlyphs (const int startIndex, const int num, const float x, const float y, const float width, const float height, const Justification& justification) { jassert (num >= 0 && startIndex >= 0); if (glyphs.size() > 0 && num > 0) { const Rectangle<float> bb (getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified | Justification::horizontallyCentred))); float deltaX = 0.0f; if (justification.testFlags (Justification::horizontallyJustified)) deltaX = x - bb.getX(); else if (justification.testFlags (Justification::horizontallyCentred)) deltaX = x + (width - bb.getWidth()) * 0.5f - bb.getX(); else if (justification.testFlags (Justification::right)) deltaX = (x + width) - bb.getRight(); else deltaX = x - bb.getX(); float deltaY = 0.0f; if (justification.testFlags (Justification::top)) deltaY = y - bb.getY(); else if (justification.testFlags (Justification::bottom)) deltaY = (y + height) - bb.getBottom(); else deltaY = y + (height - bb.getHeight()) * 0.5f - bb.getY(); moveRangeOfGlyphs (startIndex, num, deltaX, deltaY); if (justification.testFlags (Justification::horizontallyJustified)) { int lineStart = 0; float baseY = glyphs.getUnchecked (startIndex)->getBaselineY(); int i; for (i = 0; i < num; ++i) { const float glyphY = glyphs.getUnchecked (startIndex + i)->getBaselineY(); if (glyphY != baseY) { spreadOutLine (startIndex + lineStart, i - lineStart, width); lineStart = i; baseY = glyphY; } } if (i > lineStart) spreadOutLine (startIndex + lineStart, i - lineStart, width); } } }
void GlyphArrangement::addJustifiedText (const Font& font, const String& text, float x, float y, const float maxLineWidth, Justification horizontalLayout) { int lineStartIndex = glyphs.size(); addLineOfText (font, text, x, y); const float originalY = y; while (lineStartIndex < glyphs.size()) { int i = lineStartIndex; if (glyphs.getReference(i).getCharacter() != '\n' && glyphs.getReference(i).getCharacter() != '\r') ++i; const float lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth; int lastWordBreakIndex = -1; while (i < glyphs.size()) { const PositionedGlyph& pg = glyphs.getReference (i); const juce_wchar c = pg.getCharacter(); if (c == '\r' || c == '\n') { ++i; if (c == '\r' && i < glyphs.size() && glyphs.getReference(i).getCharacter() == '\n') ++i; break; } if (pg.isWhitespace()) { lastWordBreakIndex = i + 1; } else if (pg.getRight() - 0.0001f >= lineMaxX) { if (lastWordBreakIndex >= 0) i = lastWordBreakIndex; break; } ++i; } const float currentLineStartX = glyphs.getReference (lineStartIndex).getLeft(); float currentLineEndX = currentLineStartX; for (int j = i; --j >= lineStartIndex;) { if (! glyphs.getReference (j).isWhitespace()) { currentLineEndX = glyphs.getReference (j).getRight(); break; } } float deltaX = 0.0f; if (horizontalLayout.testFlags (Justification::horizontallyJustified)) spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth); else if (horizontalLayout.testFlags (Justification::horizontallyCentred)) deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f; else if (horizontalLayout.testFlags (Justification::right)) deltaX = maxLineWidth - (currentLineEndX - currentLineStartX); moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex, x + deltaX - currentLineStartX, y - originalY); lineStartIndex = i; y += font.getHeight(); } }
void GlyphArrangement::addFittedText (const Font& f, const String& text, const float x, const float y, const float width, const float height, const Justification& layout, int maximumLines, const float minimumHorizontalScale) { // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0 jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f); 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.ensureStorageAllocated (glyphs.size() + ga.glyphs.size()); for (int i = 0; i < ga.glyphs.size(); ++i) glyphs.add (ga.glyphs.getUnchecked (i)); ga.glyphs.clear (false); return; } int startIndex = glyphs.size(); addLineOfText (f, text.trim(), x, y); if (glyphs.size() > startIndex) { float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() - glyphs.getUnchecked (startIndex)->getLeft(); if (lineWidth <= 0) return; if (lineWidth * minimumHorizontalScale < width) { if (lineWidth > width) stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex, width / lineWidth); 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) { ++numLines; const float newFontHeight = height / (float) numLines; if (newFontHeight < font.getHeight()) { font.setHeight (jmax (8.0f, newFontHeight)); removeRangeOfGlyphs (startIndex, -1); addLineOfText (font, txt, x, y); lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() - glyphs.getUnchecked (startIndex)->getLeft(); } if (numLines > lineWidth / width || newFontHeight < 8.0f) break; } if (numLines < 1) numLines = 1; float lineY = y; float widthPerLine = lineWidth / numLines; int lastLineStartIndex = 0; for (int line = 0; line < numLines; ++line) { int i = startIndex; lastLineStartIndex = i; float lineStartX = glyphs.getUnchecked (startIndex)->getLeft(); if (line == numLines - 1) { widthPerLine = width; i = glyphs.size(); } else { while (i < glyphs.size()) { lineWidth = (glyphs.getUnchecked (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.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width) { if (glyphs.getUnchecked (i)->isWhitespace() || glyphs.getUnchecked (i)->getCharacter() == '-') { ++i; break; } } else { // can't find a suitable break, so try looking backwards.. i = searchStartIndex; for (int back = 1; back < jmin (5, i - startIndex - 1); ++back) { if (glyphs.getUnchecked (i - back)->isWhitespace() || glyphs.getUnchecked (i - back)->getCharacter() == '-') { i -= back - 1; break; } } break; } ++i; } break; } ++i; } int wsStart = i; while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace()) --wsStart; int wsEnd = i; while (wsEnd < glyphs.size() && glyphs.getUnchecked (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); } } }