void paint (Graphics& g) override { jassert (dynamic_cast <CodeEditorComponent*> (getParentComponent()) != nullptr); const CodeEditorComponent& editor = *static_cast <CodeEditorComponent*> (getParentComponent()); g.fillAll (editor.findColour (CodeEditorComponent::backgroundColourId) .overlaidWith (editor.findColour (lineNumberBackgroundId))); const Rectangle<int> clip (g.getClipBounds()); const int lineH = editor.lineHeight; const float lineHeightFloat = (float) lineH; const int firstLineToDraw = jmax (0, clip.getY() / lineH); const int lastLineToDraw = jmin (editor.lines.size(), clip.getBottom() / lineH + 1, lastNumLines - editor.firstLineOnScreen); const Font lineNumberFont (editor.getFont().withHeight (jmin (13.0f, lineHeightFloat * 0.8f))); const float w = getWidth() - 2.0f; GlyphArrangement ga; for (int i = firstLineToDraw; i < lastLineToDraw; ++i) ga.addFittedText (lineNumberFont, String (editor.firstLineOnScreen + i + 1), 0, (float) (lineH * i), w, lineHeightFloat, Justification::centredRight, 1, 0.2f); g.setColour (editor.findColour (lineNumberTextId)); ga.draw (g); }
static float getAverageY (const Font& font, const char* chars, bool getTop) { GlyphArrangement ga; ga.addLineOfText (font, chars, 0, 0); Array<float> y; DefaultElementComparator<float> sorter; for (int i = 0; i < ga.getNumGlyphs(); ++i) { Path p; ga.getGlyph (i).createPath (p); Rectangle<float> bounds (p.getBounds()); if (! p.isEmpty()) y.addSorted (sorter, getTop ? bounds.getY() : bounds.getBottom()); } float median = y[y.size() / 2]; float total = 0; int num = 0; for (int i = 0; i < y.size(); ++i) { if (std::abs (median - y.getUnchecked(i)) < 0.05f * (float) standardHeight) { total += y.getUnchecked(i); ++num; } } return num < 4 ? 0.0f : total / (num * (float) standardHeight); }
//============================================================================== void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY, const Justification& justification) const { if (text.isNotEmpty() && startX < context.getClipBounds().getRight()) { GlyphArrangement arr; arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY); // Don't pass any vertical placement flags to this method - they'll be ignored. jassert (justification.getOnlyVerticalFlags() == 0); const int flags = justification.getOnlyHorizontalFlags(); if (flags != Justification::left) { float w = arr.getBoundingBox (0, -1, true).getWidth(); if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0) w /= 2.0f; arr.draw (*this, AffineTransform::translation (-w, 0)); } else { arr.draw (*this); } } }
void Graphics::drawTextAsPath (const String& text, const AffineTransform& transform) const { if (text.isNotEmpty()) { GlyphArrangement arr; arr.addLineOfText (context.getFont(), text, 0.0f, 0.0f); arr.draw (*this, transform); } }
void CtrlrTabsLF::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) { const Rectangle<float> area (button.getTextArea().toFloat()); float length = area.getWidth(); float depth = area.getHeight(); if (button.getTabbedButtonBar().isVertical()) std::swap (length, depth); Font otherTabFont = owner.getOwner().getOwner().getOwner().getFontManager().getFontFromString (owner.getProperty(Ids::uiTabsTabFont)); Font activeTabFont = owner.getOwner().getOwner().getOwner().getFontManager().getFontFromString (owner.getProperty(Ids::uiTabsFrontTabFont)); otherTabFont.setUnderline (button.hasKeyboardFocus (false)); activeTabFont.setUnderline (button.hasKeyboardFocus (false)); GlyphArrangement textLayout; textLayout.addFittedText (button.isFrontTab() ? activeTabFont : otherTabFont, button.getButtonText().trim(), 0.0f, 0.0f, (float) length, (float) depth, Justification::centred, jmax<int> (1, depth / 12)); AffineTransform t; switch (button.getTabbedButtonBar().getOrientation()) { case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break; case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break; case TabbedButtonBar::TabsAtTop: case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break; default: jassertfalse; break; } Colour col; if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) || isColourSpecified (TabbedButtonBar::frontTextColourId))) col = findColour (TabbedButtonBar::frontTextColourId); else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) || isColourSpecified (TabbedButtonBar::tabTextColourId)) col = findColour (TabbedButtonBar::tabTextColourId); else col = button.getTabBackgroundColour().contrasting(); const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; g.setColour (col.withMultipliedAlpha (alpha)); textLayout.draw (g, t); }
//============================================================================== void DrawableText::paint (Graphics& g) { transformContextToCorrectOrigin (g); g.setColour (colour); GlyphArrangement ga; const AffineTransform transform (getArrangementAndTransform (ga)); ga.draw (g, transform); }
//============================================================================== void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY) const { if (text.isNotEmpty() && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY); arr.draw (*this); } }
void UploadWindow::paint(Graphics& g) { g.fillAll(Colours::black); Colour border = Colours::wheat; juce::Rectangle<int> rc(0, 0, getWidth(), getHeight()); for (int i = 0; i < 4; i++) { g.setColour(i == 0 ? Colours::black : border); g.drawRect(rc.getX(), rc.getY(), rc.getWidth(), rc.getHeight()); rc.reduce(1, 1); border = border.brighter(0.4f); } ColourGradient gf(Colours::red, 0, getHeight()/2.0f, Colours::darkred, float(getWidth()), getHeight()/2.0f, false); FillType ft(gf); int cx = getWidth() / 2; int cy = getHeight() / 2; const float r = 12.0f; for (int i = 3; i >= 0; i--) { if (i % 2 != 0) g.setFillType(ft); else g.setColour(Colours::white); g.fillEllipse(cx - (r * i), cy - (r * i), (r * i) * 2, (r * i) * 2); } g.setFillType(Colours::transparentWhite); if (smugMug.isUploading()) { int64 n = smugMug.getTotalbytesUploaded(); int64 d = smugMug.getTotalBytesToUpload(); double percent = (d == 0) ? 0 : (double(n)/double(d)*100); GlyphArrangement glyphs; glyphs.addLineOfText(Font(25.0f, Font::bold), String(percent, 1) + ("%"), 0, 0); Path p; glyphs.createPath(p); juce::Rectangle<float> bounds = p.getBounds(); float cx = getWidth() / 2.0f - bounds.getWidth() / 2.0f - bounds.getX(); float cy = getHeight() / 2.0f - bounds.getHeight() / 2.0f - bounds.getY(); AffineTransform trans = AffineTransform::translation(cx, cy); g.setColour(Colours::black); g.fillPath(p, trans); g.setColour(Colours::white); g.strokePath(p, PathStrokeType(1), trans); } }
Rectangle<int> IntrojucerLookAndFeel::getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle<int>& textArea, Component& comp) { GlyphArrangement textLayout; createTabTextLayout (button, textArea, textLayout); const int textWidth = (int) textLayout.getBoundingBox (0, -1, false).getWidth(); const int extraSpace = jmax (0, textArea.getWidth() - (textWidth + comp.getWidth())) / 2; textArea.removeFromRight (extraSpace); textArea.removeFromLeft (extraSpace); return textArea.removeFromRight (comp.getWidth()); }
void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const { if (text.isNotEmpty() && startX < context->getClipBounds().getRight()) { GlyphArrangement arr; arr.addJustifiedText (context->getFont(), text, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::left); arr.draw (*this); } }
void paint (Graphics& g) override { double startTime = 0.0; { // A ScopedSaveState will return the Graphics context to the state it was at the time of // construction when it goes out of scope. We use it here to avoid clipping the fps text const Graphics::ScopedSaveState state (g); if (controls.clipToRectangle.getToggleState()) clipToRectangle (g); if (controls.clipToPath .getToggleState()) clipToPath (g); if (controls.clipToImage .getToggleState()) clipToImage (g); g.setImageResamplingQuality (controls.quality.getToggleState() ? Graphics::highResamplingQuality : Graphics::mediumResamplingQuality); // take a note of the time before the render startTime = Time::getMillisecondCounterHiRes(); // then let the demo draw itself.. drawDemo (g); } double now = Time::getMillisecondCounterHiRes(); double filtering = 0.08; const double elapsedMs = now - startTime; averageTimeMs += (elapsedMs - averageTimeMs) * filtering; const double sinceLastRender = now - lastRenderStartTime; lastRenderStartTime = now; const double effectiveFPS = 1000.0 / averageTimeMs; const double actualFPS = sinceLastRender > 0 ? (1000.0 / sinceLastRender) : 0; averageActualFPS += (actualFPS - averageActualFPS) * filtering; GlyphArrangement ga; ga.addFittedText (displayFont, "Time: " + String (averageTimeMs, 2) + " ms\nEffective FPS: " + String (effectiveFPS, 1) + "\nActual FPS: " + String (averageActualFPS, 1), 0, 10.0f, getWidth() - 10.0f, (float) getHeight(), Justification::topRight, 3); g.setColour (Colours::white.withAlpha (0.5f)); g.fillRect (ga.getBoundingBox (0, ga.getNumGlyphs(), true).getSmallestIntegerContainer().expanded (4)); g.setColour (Colours::black); ga.draw (g); }
void Graphics::drawText (const String& text, const Rectangle<float>& area, Justification justificationType, bool useEllipsesIfTooBig) const { if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer())) { GlyphArrangement arr; arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f, area.getWidth(), useEllipsesIfTooBig); arr.justifyGlyphs (0, arr.getNumGlyphs(), area.getX(), area.getY(), area.getWidth(), area.getHeight(), justificationType); arr.draw (*this); } }
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 IntrojucerLookAndFeel::createTabTextLayout (const TabBarButton& button, const Rectangle<int>& textArea, GlyphArrangement& textLayout) { Font font (textArea.getHeight() * 0.5f); font.setUnderline (button.hasKeyboardFocus (false)); textLayout.addFittedText (font, button.getButtonText().trim(), (float) textArea.getX(), (float) textArea.getY(), (float) textArea.getWidth(), (float) textArea.getHeight(), Justification::centred, 1); }
void Graphics::drawFittedText (const String& text, const Rectangle<int>& area, const Justification& justification, const int maximumNumberOfLines, const float minimumHorizontalScale) const { if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area)) { GlyphArrangement arr; arr.addFittedText (context.getFont(), text, (float) area.getX(), (float) area.getY(), (float) area.getWidth(), (float) area.getHeight(), justification, maximumNumberOfLines, minimumHorizontalScale); arr.draw (*this); } }
void Graphics::drawText (const String& text, const int x, const int y, const int width, const int height, const Justification& justificationType, const bool useEllipsesIfTooBig) const { if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle<int> (x, y, width, height))) { GlyphArrangement arr; arr.addCurtailedLineOfText (context->getFont(), text, 0.0f, 0.0f, (float) width, useEllipsesIfTooBig); arr.justifyGlyphs (0, arr.getNumGlyphs(), (float) x, (float) y, (float) width, (float) height, justificationType); arr.draw (*this); } }
void IntrojucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) { const Rectangle<int> activeArea (button.getActiveArea()); const Colour bkg (getTabBackgroundColour (button)); g.setGradientFill (ColourGradient (bkg.brighter (0.1f), 0, (float) activeArea.getY(), bkg.darker (0.1f), 0, (float) activeArea.getBottom(), false)); g.fillRect (activeArea); g.setColour (button.findColour (mainBackgroundColourId).darker (0.3f)); g.drawRect (activeArea); GlyphArrangement textLayout; createTabTextLayout (button, button.getTextArea(), textLayout); const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; g.setColour (bkg.contrasting().withMultipliedAlpha (alpha)); textLayout.draw (g); }
const AffineTransform DrawableText::getArrangementAndTransform (GlyphArrangement& glyphs) const { const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength(); const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength(); glyphs.addFittedText (scaledFont, text, 0, 0, w, h, justification, 0x100000); return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].getX(), resolvedPoints[0].getY(), w, 0, resolvedPoints[1].getX(), resolvedPoints[1].getY(), 0, h, resolvedPoints[2].getX(), resolvedPoints[2].getY()); }
void paint(Graphics& g) { // The following code is basically the same as // Graphics::drawMultiLineText but with centered text int startX = 20; int baselineY = getHeight() / 2; int maximumLineWidth = getWidth() - 40; LowLevelGraphicsContext& context = g.getInternalContext(); if (textToShow.isNotEmpty() && startX < context.getClipBounds().getRight()) { GlyphArrangement arr; arr.addJustifiedText(context.getFont(), textToShow, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::centred); arr.draw(g); } }
void Graphics::drawFittedText (const String& text, const int x, const int y, const int width, const int height, const Justification& justification, const int maximumNumberOfLines, const float minimumHorizontalScale) const { if (text.isNotEmpty() && width > 0 && height > 0 && context->clipRegionIntersects (Rectangle<int> (x, y, width, height))) { GlyphArrangement arr; arr.addFittedText (context->getFont(), text, (float) x, (float) y, (float) width, (float) height, justification, maximumNumberOfLines, minimumHorizontalScale); arr.draw (*this); } }
const int EdoChatWindowMessage::getMessageHeight(const int parentWidth, const Justification justification) { GlyphArrangement messageGlyphs; float top,left,right,bottom; applyOptions(); messageGlyphs.clear(); messageGlyphs.addJustifiedText ( EdoRestoreFont (getPropertyString(EWND_MESSAGE_FONT), Font(14)), messageLabel->getText(), 0, 0, (float)parentWidth, justification ); messageGlyphs.getBoundingBox (0, -1, left, top, right, bottom, true); messageHeight = roundFloatToInt ((bottom - top) + SPACER); // Log (String::formatted (T("EdoChatWindowMessage::getMessageHeight parentWidth: %d height: %d"), parentWidth, messageHeight)); if (avatar->isVisible()) { if (messageHeight < (avatar->getHeight()+16)) { messageHeight = avatar->getHeight()+16; return (messageHeight+SPACER); } else { return (messageHeight+SPACER); } } else { return (messageHeight+SPACER); } }
void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) { if (rowIsSelected) g.fillAll (findColour (TextEditor::highlightColourId)); String name, desc; bool isBlacklisted = false; if (row >= list.getNumTypes()) { isBlacklisted = true; name = list.getBlacklistedFiles() [row - list.getNumTypes()]; desc = TRANS("Deactivated after failing to initialise correctly"); } else if (const PluginDescription* const pd = list.getType (row)) { name = pd->name; desc << pd->pluginFormatName << (pd->isInstrument ? " instrument" : " effect") << " - " << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") << " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); if (pd->manufacturerName.isNotEmpty()) desc << " - " << pd->manufacturerName; if (pd->version.isNotEmpty()) desc << " - " << pd->version; if (pd->category.isNotEmpty()) desc << " - category: '" << pd->category << '\''; } if (name.isNotEmpty()) { GlyphArrangement ga; ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), name, 8.0f, height * 0.8f, width - 10.0f, true); g.setColour (isBlacklisted ? Colours::red : Colours::black); ga.draw (g); const Rectangle<float> bb (ga.getBoundingBox (0, -1, false)); ga.clear(); ga.addCurtailedLineOfText (Font (height * 0.6f), desc, jmax (bb.getRight() + 10.0f, width / 3.0f), height * 0.8f, width - bb.getRight() - 12.0f, true); g.setColour (isBlacklisted ? Colours::red : Colours::grey); ga.draw (g); } }
void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) { if (rowIsSelected) g.fillAll (findColour (TextEditor::highlightColourId)); const PluginDescription* const pd = list.getType (row); if (pd != 0) { GlyphArrangement ga; ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true); g.setColour (Colours::black); ga.draw (g); float x, y, r, b; ga.getBoundingBox (0, -1, x, y, r, b, false); String desc; desc << pd->pluginFormatName << (pd->isInstrument ? " instrument" : " effect") << " - " << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") << " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); if (pd->manufacturerName.isNotEmpty()) desc << " - " << pd->manufacturerName; if (pd->version.isNotEmpty()) desc << " - " << pd->version; if (pd->category.isNotEmpty()) desc << " - category: '" << pd->category << '\''; g.setColour (Colours::grey); ga.clear(); ga.addCurtailedLineOfText (Font (height * 0.6f), desc, r + 10.0f, height * 0.8f, width - r - 12.0f, true); ga.draw (g); } }
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); } } }