255 lines
7.5 KiB
C++
255 lines
7.5 KiB
C++
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||
|
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-6-licence
|
||
|
Privacy Policy: www.juce.com/juce-privacy-policy
|
||
|
|
||
|
Or: You may also use this code under the terms of the GPL v3 (see
|
||
|
www.gnu.org/licenses).
|
||
|
|
||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||
|
DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
namespace juce
|
||
|
{
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
int getLength (const Array<AttributedString::Attribute>& atts) noexcept
|
||
|
{
|
||
|
return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0;
|
||
|
}
|
||
|
|
||
|
void splitAttributeRanges (Array<AttributedString::Attribute>& atts, int position)
|
||
|
{
|
||
|
for (int i = atts.size(); --i >= 0;)
|
||
|
{
|
||
|
const auto& att = atts.getUnchecked (i);
|
||
|
auto offset = position - att.range.getStart();
|
||
|
|
||
|
if (offset >= 0)
|
||
|
{
|
||
|
if (offset > 0 && position < att.range.getEnd())
|
||
|
{
|
||
|
atts.insert (i + 1, AttributedString::Attribute (att));
|
||
|
atts.getReference (i).range.setEnd (position);
|
||
|
atts.getReference (i + 1).range.setStart (position);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange)
|
||
|
{
|
||
|
newRange = newRange.getIntersectionWith ({ 0, getLength (atts) });
|
||
|
|
||
|
if (! newRange.isEmpty())
|
||
|
{
|
||
|
splitAttributeRanges (atts, newRange.getStart());
|
||
|
splitAttributeRanges (atts, newRange.getEnd());
|
||
|
}
|
||
|
|
||
|
return newRange;
|
||
|
}
|
||
|
|
||
|
void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts)
|
||
|
{
|
||
|
for (int i = atts.size() - 1; --i >= 0;)
|
||
|
{
|
||
|
auto& a1 = atts.getReference (i);
|
||
|
auto& a2 = atts.getReference (i + 1);
|
||
|
|
||
|
if (a1.colour == a2.colour && a1.font == a2.font)
|
||
|
{
|
||
|
a1.range.setEnd (a2.range.getEnd());
|
||
|
atts.remove (i + 1);
|
||
|
|
||
|
if (i < atts.size() - 1)
|
||
|
++i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void appendRange (Array<AttributedString::Attribute>& atts,
|
||
|
int length, const Font* f, const Colour* c)
|
||
|
{
|
||
|
if (atts.size() == 0)
|
||
|
{
|
||
|
atts.add ({ Range<int> (0, length), f != nullptr ? *f : Font(), c != nullptr ? *c : Colour (0xff000000) });
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
auto start = getLength (atts);
|
||
|
atts.add ({ Range<int> (start, start + length),
|
||
|
f != nullptr ? *f : atts.getReference (atts.size() - 1).font,
|
||
|
c != nullptr ? *c : atts.getReference (atts.size() - 1).colour });
|
||
|
|
||
|
mergeAdjacentRanges (atts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void applyFontAndColour (Array<AttributedString::Attribute>& atts,
|
||
|
Range<int> range, const Font* f, const Colour* c)
|
||
|
{
|
||
|
range = splitAttributeRanges (atts, range);
|
||
|
|
||
|
for (auto& att : atts)
|
||
|
{
|
||
|
if (range.getStart() < att.range.getEnd())
|
||
|
{
|
||
|
if (range.getEnd() <= att.range.getStart())
|
||
|
break;
|
||
|
|
||
|
if (c != nullptr) att.colour = *c;
|
||
|
if (f != nullptr) att.font = *f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mergeAdjacentRanges (atts);
|
||
|
}
|
||
|
|
||
|
void truncate (Array<AttributedString::Attribute>& atts, int newLength)
|
||
|
{
|
||
|
splitAttributeRanges (atts, newLength);
|
||
|
|
||
|
for (int i = atts.size(); --i >= 0;)
|
||
|
if (atts.getReference (i).range.getStart() >= newLength)
|
||
|
atts.remove (i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept
|
||
|
: range (r), font (f), colour (c)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
void AttributedString::setText (const String& newText)
|
||
|
{
|
||
|
auto newLength = newText.length();
|
||
|
auto oldLength = getLength (attributes);
|
||
|
|
||
|
if (newLength > oldLength)
|
||
|
appendRange (attributes, newLength - oldLength, nullptr, nullptr);
|
||
|
else if (newLength < oldLength)
|
||
|
truncate (attributes, newLength);
|
||
|
|
||
|
text = newText;
|
||
|
}
|
||
|
|
||
|
void AttributedString::append (const String& textToAppend)
|
||
|
{
|
||
|
text += textToAppend;
|
||
|
appendRange (attributes, textToAppend.length(), nullptr, nullptr);
|
||
|
}
|
||
|
|
||
|
void AttributedString::append (const String& textToAppend, const Font& font)
|
||
|
{
|
||
|
text += textToAppend;
|
||
|
appendRange (attributes, textToAppend.length(), &font, nullptr);
|
||
|
}
|
||
|
|
||
|
void AttributedString::append (const String& textToAppend, Colour colour)
|
||
|
{
|
||
|
text += textToAppend;
|
||
|
appendRange (attributes, textToAppend.length(), nullptr, &colour);
|
||
|
}
|
||
|
|
||
|
void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
|
||
|
{
|
||
|
text += textToAppend;
|
||
|
appendRange (attributes, textToAppend.length(), &font, &colour);
|
||
|
}
|
||
|
|
||
|
void AttributedString::append (const AttributedString& other)
|
||
|
{
|
||
|
auto originalLength = getLength (attributes);
|
||
|
auto originalNumAtts = attributes.size();
|
||
|
text += other.text;
|
||
|
attributes.addArray (other.attributes);
|
||
|
|
||
|
for (auto i = originalNumAtts; i < attributes.size(); ++i)
|
||
|
attributes.getReference (i).range += originalLength;
|
||
|
|
||
|
mergeAdjacentRanges (attributes);
|
||
|
}
|
||
|
|
||
|
void AttributedString::clear()
|
||
|
{
|
||
|
text.clear();
|
||
|
attributes.clear();
|
||
|
}
|
||
|
|
||
|
void AttributedString::setJustification (Justification newJustification) noexcept
|
||
|
{
|
||
|
justification = newJustification;
|
||
|
}
|
||
|
|
||
|
void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
|
||
|
{
|
||
|
wordWrap = newWordWrap;
|
||
|
}
|
||
|
|
||
|
void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
|
||
|
{
|
||
|
readingDirection = newReadingDirection;
|
||
|
}
|
||
|
|
||
|
void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
|
||
|
{
|
||
|
lineSpacing = newLineSpacing;
|
||
|
}
|
||
|
|
||
|
void AttributedString::setColour (Range<int> range, Colour colour)
|
||
|
{
|
||
|
applyFontAndColour (attributes, range, nullptr, &colour);
|
||
|
}
|
||
|
|
||
|
void AttributedString::setFont (Range<int> range, const Font& font)
|
||
|
{
|
||
|
applyFontAndColour (attributes, range, &font, nullptr);
|
||
|
}
|
||
|
|
||
|
void AttributedString::setColour (Colour colour)
|
||
|
{
|
||
|
setColour ({ 0, getLength (attributes) }, colour);
|
||
|
}
|
||
|
|
||
|
void AttributedString::setFont (const Font& font)
|
||
|
{
|
||
|
setFont ({ 0, getLength (attributes) }, font);
|
||
|
}
|
||
|
|
||
|
void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
|
||
|
{
|
||
|
if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
|
||
|
{
|
||
|
jassert (text.length() == getLength (attributes));
|
||
|
|
||
|
if (! g.getInternalContext().drawTextLayout (*this, area))
|
||
|
{
|
||
|
TextLayout layout;
|
||
|
layout.createLayout (*this, area.getWidth());
|
||
|
layout.draw (g, area);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace juce
|