Move some methods out of the global scope.
This commit is contained in:
parent
c33ff2baba
commit
d71d2121b1
5 changed files with 450 additions and 385 deletions
174
ui/text/text.cpp
174
ui/text/text.cpp
|
|
@ -21,10 +21,14 @@ namespace Text {
|
|||
namespace {
|
||||
|
||||
constexpr auto kStringLinkIndexShift = uint16(0x8000);
|
||||
constexpr auto kMaxDiacAfterSymbol = 2;
|
||||
|
||||
Qt::LayoutDirection StringDirection(const QString &str, int32 from, int32 to) {
|
||||
const ushort *p = reinterpret_cast<const ushort*>(str.unicode()) + from;
|
||||
const ushort *end = p + (to - from);
|
||||
Qt::LayoutDirection StringDirection(
|
||||
const QString &str,
|
||||
int from,
|
||||
int to) {
|
||||
auto p = reinterpret_cast<const ushort*>(str.unicode()) + from;
|
||||
const auto end = p + (to - from);
|
||||
while (p < end) {
|
||||
uint ucs4 = *p;
|
||||
if (QChar::isHighSurrogate(ucs4) && p < end - 1) {
|
||||
|
|
@ -98,7 +102,9 @@ TextWithEntities PrepareRichFromRich(
|
|||
return result;
|
||||
}
|
||||
|
||||
QFixed ComputeStopAfter(const TextParseOptions &options, const style::TextStyle &st) {
|
||||
QFixed ComputeStopAfter(
|
||||
const TextParseOptions &options,
|
||||
const style::TextStyle &st) {
|
||||
return (options.maxw > 0 && options.maxh > 0)
|
||||
? ((options.maxh / st.font->height) + 1) * options.maxw
|
||||
: QFIXED_MAX;
|
||||
|
|
@ -112,11 +118,17 @@ bool ComputeCheckTilde(const style::TextStyle &st) {
|
|||
&& (font->f.family() == qstr("DAOpenSansRegular"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
bool IsParagraphSeparator(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case QChar::LineFeed:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool chIsBad(QChar ch) {
|
||||
bool IsBad(QChar ch) {
|
||||
return (ch == 0)
|
||||
|| (ch >= 8232 && ch < 8237)
|
||||
|| (ch >= 65024 && ch < 65040 && ch != 65039)
|
||||
|
|
@ -130,9 +142,13 @@ bool chIsBad(QChar ch) {
|
|||
&& !Platform::IsMac10_12OrGreater()
|
||||
&& ch >= 0x0B00
|
||||
&& ch <= 0x0B7F
|
||||
&& chIsDiac(ch));
|
||||
&& IsDiac(ch));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
||||
QString textcmdSkipBlock(ushort w, ushort h) {
|
||||
static QString cmd(5, TextCommand);
|
||||
cmd[1] = QChar(TextCommandSkipBlock);
|
||||
|
|
@ -745,18 +761,18 @@ bool Parser::readCommand() {
|
|||
void Parser::parseCurrentChar() {
|
||||
_ch = ((_ptr < _end) ? *_ptr : 0);
|
||||
_emojiLookback = 0;
|
||||
const auto isNewLine = _multiline && chIsNewline(_ch);
|
||||
const auto isSpace = chIsSpace(_ch);
|
||||
const auto isDiac = chIsDiac(_ch);
|
||||
const auto isNewLine = _multiline && IsNewline(_ch);
|
||||
const auto isSpace = IsSpace(_ch);
|
||||
const auto isDiac = IsDiac(_ch);
|
||||
const auto isTilde = _checkTilde && (_ch == '~');
|
||||
const auto skip = [&] {
|
||||
if (chIsBad(_ch) || _ch.isLowSurrogate()) {
|
||||
if (IsBad(_ch) || _ch.isLowSurrogate()) {
|
||||
return true;
|
||||
} else if (_ch == 0xFE0F && Platform::IsMac()) {
|
||||
// Some sequences like 0x0E53 0xFE0F crash OS X harfbuzz text processing :(
|
||||
return true;
|
||||
} else if (isDiac) {
|
||||
if (_lastSkipped || _emoji || ++_diacs > chMaxDiacAfterSymbol()) {
|
||||
if (_lastSkipped || _emoji || ++_diacs > kMaxDiacAfterSymbol) {
|
||||
return true;
|
||||
}
|
||||
} else if (_ch.isHighSurrogate()) {
|
||||
|
|
@ -891,10 +907,10 @@ void Parser::trimSourceRange() {
|
|||
_source.entities,
|
||||
_end - _start);
|
||||
|
||||
while (_ptr != _end && chIsTrimmed(*_ptr, _rich) && _ptr != _start + firstMonospaceOffset) {
|
||||
while (_ptr != _end && IsTrimmed(*_ptr, _rich) && _ptr != _start + firstMonospaceOffset) {
|
||||
++_ptr;
|
||||
}
|
||||
while (_ptr != _end && chIsTrimmed(*(_end - 1), _rich)) {
|
||||
while (_ptr != _end && IsTrimmed(*(_end - 1), _rich)) {
|
||||
--_end;
|
||||
}
|
||||
}
|
||||
|
|
@ -2838,7 +2854,7 @@ void String::setMarkedText(const style::TextStyle &st, const TextWithEntities &t
|
|||
// for (const QChar *ch = text.constData(), *e = ch + text.size(); ch != e; ++ch) {
|
||||
// if (*ch == TextCommand) {
|
||||
// break;
|
||||
// } else if (chIsNewline(*ch)) {
|
||||
// } else if (IsNewline(*ch)) {
|
||||
// newText.append("},").append(*ch).append("\t{ ");
|
||||
// } else {
|
||||
// if (ch->isHighSurrogate() || ch->isLowSurrogate()) {
|
||||
|
|
@ -3103,31 +3119,31 @@ TextSelection String::adjustSelection(TextSelection selection, TextSelectType se
|
|||
if (from < _text.size() && from <= to) {
|
||||
if (to > _text.size()) to = _text.size();
|
||||
if (selectType == TextSelectType::Paragraphs) {
|
||||
if (!chIsParagraphSeparator(_text.at(from))) {
|
||||
while (from > 0 && !chIsParagraphSeparator(_text.at(from - 1))) {
|
||||
if (!IsParagraphSeparator(_text.at(from))) {
|
||||
while (from > 0 && !IsParagraphSeparator(_text.at(from - 1))) {
|
||||
--from;
|
||||
}
|
||||
}
|
||||
if (to < _text.size()) {
|
||||
if (chIsParagraphSeparator(_text.at(to))) {
|
||||
if (IsParagraphSeparator(_text.at(to))) {
|
||||
++to;
|
||||
} else {
|
||||
while (to < _text.size() && !chIsParagraphSeparator(_text.at(to))) {
|
||||
while (to < _text.size() && !IsParagraphSeparator(_text.at(to))) {
|
||||
++to;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (selectType == TextSelectType::Words) {
|
||||
if (!chIsWordSeparator(_text.at(from))) {
|
||||
while (from > 0 && !chIsWordSeparator(_text.at(from - 1))) {
|
||||
if (!IsWordSeparator(_text.at(from))) {
|
||||
while (from > 0 && !IsWordSeparator(_text.at(from - 1))) {
|
||||
--from;
|
||||
}
|
||||
}
|
||||
if (to < _text.size()) {
|
||||
if (chIsWordSeparator(_text.at(to))) {
|
||||
if (IsWordSeparator(_text.at(to))) {
|
||||
++to;
|
||||
} else {
|
||||
while (to < _text.size() && !chIsWordSeparator(_text.at(to))) {
|
||||
while (to < _text.size() && !IsWordSeparator(_text.at(to))) {
|
||||
++to;
|
||||
}
|
||||
}
|
||||
|
|
@ -3355,5 +3371,113 @@ void String::clearFields() {
|
|||
_startDir = Qt::LayoutDirectionAuto;
|
||||
}
|
||||
|
||||
bool IsWordSeparator(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case QChar::Space:
|
||||
case QChar::LineFeed:
|
||||
case '.':
|
||||
case ',':
|
||||
case '?':
|
||||
case '!':
|
||||
case '@':
|
||||
case '#':
|
||||
case '$':
|
||||
case ':':
|
||||
case ';':
|
||||
case '-':
|
||||
case '<':
|
||||
case '>':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '=':
|
||||
case '/':
|
||||
case '+':
|
||||
case '%':
|
||||
case '&':
|
||||
case '^':
|
||||
case '*':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '`':
|
||||
case '~':
|
||||
case '|':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAlmostLinkEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case '?':
|
||||
case ',':
|
||||
case '.':
|
||||
case '"':
|
||||
case ':':
|
||||
case '!':
|
||||
case '\'':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLinkEnd(QChar ch) {
|
||||
return (ch == TextCommand)
|
||||
|| IsBad(ch)
|
||||
|| IsSpace(ch)
|
||||
|| IsNewline(ch)
|
||||
|| ch.isLowSurrogate()
|
||||
|| ch.isHighSurrogate();
|
||||
}
|
||||
|
||||
bool IsNewline(QChar ch) {
|
||||
return (ch == QChar::LineFeed)
|
||||
|| (ch == 156);
|
||||
}
|
||||
|
||||
bool IsSpace(QChar ch, bool rich) {
|
||||
return ch.isSpace()
|
||||
|| (ch < 32 && !(rich && ch == TextCommand))
|
||||
|| (ch == QChar::ParagraphSeparator)
|
||||
|| (ch == QChar::LineSeparator)
|
||||
|| (ch == QChar::ObjectReplacementCharacter)
|
||||
|| (ch == QChar::CarriageReturn)
|
||||
|| (ch == QChar::Tabulation)
|
||||
|| (ch == QChar(8203)/*Zero width space.*/);
|
||||
}
|
||||
|
||||
bool IsDiac(QChar ch) { // diac and variation selectors
|
||||
return (ch.category() == QChar::Mark_NonSpacing)
|
||||
|| (ch == 1652)
|
||||
|| (ch >= 64606 && ch <= 64611);
|
||||
}
|
||||
|
||||
bool IsReplacedBySpace(QChar ch) {
|
||||
// \xe2\x80[\xa8 - \xac\xad] // 8232 - 8237
|
||||
// QString from1 = QString::fromUtf8("\xe2\x80\xa8"), to1 = QString::fromUtf8("\xe2\x80\xad");
|
||||
// \xcc[\xb3\xbf\x8a] // 819, 831, 778
|
||||
// QString bad1 = QString::fromUtf8("\xcc\xb3"), bad2 = QString::fromUtf8("\xcc\xbf"), bad3 = QString::fromUtf8("\xcc\x8a");
|
||||
// [\x00\x01\x02\x07\x08\x0b-\x1f] // '\t' = 0x09
|
||||
return (/*code >= 0x00 && */ch <= 0x02)
|
||||
|| (ch >= 0x07 && ch <= 0x09)
|
||||
|| (ch >= 0x0b && ch <= 0x1f)
|
||||
|| (ch == 819)
|
||||
|| (ch == 831)
|
||||
|| (ch == 778)
|
||||
|| (ch >= 8232 && ch <= 8237);
|
||||
}
|
||||
|
||||
bool IsTrimmed(QChar ch, bool rich) {
|
||||
return (!rich || ch != TextCommand)
|
||||
&& (IsSpace(ch) || IsBad(ch));
|
||||
}
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
|
|
|||
135
ui/text/text.h
135
ui/text/text.h
|
|
@ -48,19 +48,20 @@ enum class TextSelectType {
|
|||
};
|
||||
|
||||
struct TextSelection {
|
||||
constexpr TextSelection() : from(0), to(0) {
|
||||
}
|
||||
constexpr TextSelection() = default;
|
||||
constexpr TextSelection(uint16 from, uint16 to) : from(from), to(to) {
|
||||
}
|
||||
constexpr bool empty() const {
|
||||
return from == to;
|
||||
}
|
||||
uint16 from;
|
||||
uint16 to;
|
||||
uint16 from = 0;
|
||||
uint16 to = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(TextSelection a, TextSelection b) {
|
||||
return a.from == b.from && a.to == b.to;
|
||||
}
|
||||
|
||||
inline bool operator!=(TextSelection a, TextSelection b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
|
@ -225,6 +226,15 @@ private:
|
|||
|
||||
};
|
||||
|
||||
[[nodiscard]] bool IsWordSeparator(QChar ch);
|
||||
[[nodiscard]] bool IsAlmostLinkEnd(QChar ch);
|
||||
[[nodiscard]] bool IsLinkEnd(QChar ch);
|
||||
[[nodiscard]] bool IsNewline(QChar ch);
|
||||
[[nodiscard]] bool IsSpace(QChar ch, bool rich = false);
|
||||
[[nodiscard]] bool IsDiac(QChar ch);
|
||||
[[nodiscard]] bool IsReplacedBySpace(QChar ch);
|
||||
[[nodiscard]] bool IsTrimmed(QChar ch, bool rich = false);
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
||||
|
|
@ -254,120 +264,3 @@ QString textcmdLink(const QString &url, const QString &text);
|
|||
QString textcmdStartSemibold();
|
||||
QString textcmdStopSemibold();
|
||||
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
|
||||
|
||||
inline bool chIsSpace(QChar ch, bool rich = false) {
|
||||
return ch.isSpace() || (ch < 32 && !(rich && ch == TextCommand)) || (ch == QChar::ParagraphSeparator) || (ch == QChar::LineSeparator) || (ch == QChar::ObjectReplacementCharacter) || (ch == QChar::CarriageReturn) || (ch == QChar::Tabulation) || (ch == QChar(8203)/*Zero width space.*/);
|
||||
}
|
||||
inline bool chIsDiac(QChar ch) { // diac and variation selectors
|
||||
return (ch.category() == QChar::Mark_NonSpacing) || (ch == 1652) || (ch >= 64606 && ch <= 64611);
|
||||
}
|
||||
|
||||
bool chIsBad(QChar ch);
|
||||
|
||||
inline bool chIsTrimmed(QChar ch, bool rich = false) {
|
||||
return (!rich || ch != TextCommand) && (chIsSpace(ch) || chIsBad(ch));
|
||||
}
|
||||
inline bool chReplacedBySpace(QChar ch) {
|
||||
// \xe2\x80[\xa8 - \xac\xad] // 8232 - 8237
|
||||
// QString from1 = QString::fromUtf8("\xe2\x80\xa8"), to1 = QString::fromUtf8("\xe2\x80\xad");
|
||||
// \xcc[\xb3\xbf\x8a] // 819, 831, 778
|
||||
// QString bad1 = QString::fromUtf8("\xcc\xb3"), bad2 = QString::fromUtf8("\xcc\xbf"), bad3 = QString::fromUtf8("\xcc\x8a");
|
||||
// [\x00\x01\x02\x07\x08\x0b-\x1f] // '\t' = 0x09
|
||||
return (/*code >= 0x00 && */ch <= 0x02) || (ch >= 0x07 && ch <= 0x09) || (ch >= 0x0b && ch <= 0x1f) ||
|
||||
(ch == 819) || (ch == 831) || (ch == 778) || (ch >= 8232 && ch <= 8237);
|
||||
}
|
||||
inline int32 chMaxDiacAfterSymbol() {
|
||||
return 2;
|
||||
}
|
||||
inline bool chIsNewline(QChar ch) {
|
||||
return (ch == QChar::LineFeed || ch == 156);
|
||||
}
|
||||
inline bool chIsLinkEnd(QChar ch) {
|
||||
return ch == TextCommand || chIsBad(ch) || chIsSpace(ch) || chIsNewline(ch) || ch.isLowSurrogate() || ch.isHighSurrogate();
|
||||
}
|
||||
inline bool chIsAlmostLinkEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case '?':
|
||||
case ',':
|
||||
case '.':
|
||||
case '"':
|
||||
case ':':
|
||||
case '!':
|
||||
case '\'':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsWordSeparator(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case QChar::Space:
|
||||
case QChar::LineFeed:
|
||||
case '.':
|
||||
case ',':
|
||||
case '?':
|
||||
case '!':
|
||||
case '@':
|
||||
case '#':
|
||||
case '$':
|
||||
case ':':
|
||||
case ';':
|
||||
case '-':
|
||||
case '<':
|
||||
case '>':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '=':
|
||||
case '/':
|
||||
case '+':
|
||||
case '%':
|
||||
case '&':
|
||||
case '^':
|
||||
case '*':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '`':
|
||||
case '~':
|
||||
case '|':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsSentenceEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case '.':
|
||||
case '?':
|
||||
case '!':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsSentencePartEnd(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case ',':
|
||||
case ':':
|
||||
case ';':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline bool chIsParagraphSeparator(QChar ch) {
|
||||
switch (ch.unicode()) {
|
||||
case QChar::LineFeed:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,89 +17,34 @@ namespace Text {
|
|||
namespace {
|
||||
|
||||
struct ScriptLine {
|
||||
ScriptLine() : length(0), textWidth(0) {
|
||||
}
|
||||
|
||||
int32 length;
|
||||
int length = 0;
|
||||
QFixed textWidth;
|
||||
};
|
||||
|
||||
// All members finished with "_" are internal.
|
||||
struct LineBreakHelper
|
||||
{
|
||||
LineBreakHelper()
|
||||
: glyphCount(0), maxGlyphs(INT_MAX), currentPosition(0), fontEngine(0), logClusters(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
struct LineBreakHelper {
|
||||
ScriptLine tmpData;
|
||||
ScriptLine spaceData;
|
||||
|
||||
QGlyphLayout glyphs;
|
||||
|
||||
int glyphCount;
|
||||
int maxGlyphs;
|
||||
int currentPosition;
|
||||
int glyphCount = 0;
|
||||
int maxGlyphs = INT_MAX;
|
||||
int currentPosition = 0;
|
||||
|
||||
glyph_t previousGlyph_ = 0;
|
||||
QFontEngine *previousFontEngine_ = nullptr;
|
||||
|
||||
QFixed rightBearing;
|
||||
|
||||
QFontEngine *fontEngine;
|
||||
const unsigned short *logClusters;
|
||||
QFontEngine *fontEngine = nullptr;
|
||||
const unsigned short *logClusters = nullptr;
|
||||
|
||||
inline glyph_t currentGlyph() const
|
||||
{
|
||||
Q_ASSERT(currentPosition > 0);
|
||||
Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
|
||||
|
||||
return glyphs.glyphs[logClusters[currentPosition - 1]];
|
||||
}
|
||||
|
||||
inline void saveCurrentGlyph()
|
||||
{
|
||||
if (currentPosition > 0 &&
|
||||
logClusters[currentPosition - 1] < glyphs.numGlyphs) {
|
||||
previousGlyph_ = currentGlyph(); // needed to calculate right bearing later
|
||||
previousFontEngine_ = fontEngine;
|
||||
} else {
|
||||
previousGlyph_ = 0;
|
||||
previousFontEngine_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline void calculateRightBearing(QFontEngine *engine, glyph_t glyph)
|
||||
{
|
||||
qreal rb;
|
||||
engine->getGlyphBearings(glyph, 0, &rb);
|
||||
|
||||
// We only care about negative right bearings, so we limit the range
|
||||
// of the bearing here so that we can assume it's negative in the rest
|
||||
// of the code, as well ase use QFixed(1) as a sentinel to represent
|
||||
// the state where we have yet to compute the right bearing.
|
||||
rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
|
||||
}
|
||||
|
||||
inline void calculateRightBearing()
|
||||
{
|
||||
if (currentPosition > 0 &&
|
||||
logClusters[currentPosition - 1] < glyphs.numGlyphs) {
|
||||
calculateRightBearing(fontEngine, currentGlyph());
|
||||
} else {
|
||||
rightBearing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void calculateRightBearingForPreviousGlyph()
|
||||
{
|
||||
if (previousGlyph_ > 0) {
|
||||
calculateRightBearing(previousFontEngine_, previousGlyph_);
|
||||
} else {
|
||||
rightBearing = 0;
|
||||
}
|
||||
}
|
||||
glyph_t currentGlyph() const;
|
||||
void saveCurrentGlyph();
|
||||
void calculateRightBearing(QFontEngine *engine, glyph_t glyph);
|
||||
void calculateRightBearing();
|
||||
void calculateRightBearingForPreviousGlyph();
|
||||
|
||||
// We always calculate the right bearing right before it is needed.
|
||||
// So we don't need caching / optimizations referred to delayed right bearing calculations.
|
||||
|
|
@ -113,26 +58,92 @@ struct LineBreakHelper
|
|||
|
||||
// We express the negative right bearing as an absolute number
|
||||
// so that it can be applied to the width using addition.
|
||||
inline QFixed negativeRightBearing() const
|
||||
{
|
||||
//if (rightBearing == RightBearingNotCalculated)
|
||||
// return QFixed(0);
|
||||
|
||||
return qAbs(rightBearing);
|
||||
}
|
||||
QFixed negativeRightBearing() const;
|
||||
|
||||
};
|
||||
|
||||
//const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
|
||||
|
||||
glyph_t LineBreakHelper::currentGlyph() const {
|
||||
Q_ASSERT(currentPosition > 0);
|
||||
Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
|
||||
|
||||
return glyphs.glyphs[logClusters[currentPosition - 1]];
|
||||
}
|
||||
|
||||
void LineBreakHelper::saveCurrentGlyph() {
|
||||
if (currentPosition > 0
|
||||
&& logClusters[currentPosition - 1] < glyphs.numGlyphs) {
|
||||
// needed to calculate right bearing later
|
||||
previousGlyph_ = currentGlyph();
|
||||
previousFontEngine_ = fontEngine;
|
||||
} else {
|
||||
previousGlyph_ = 0;
|
||||
previousFontEngine_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBreakHelper::calculateRightBearing(
|
||||
QFontEngine *engine,
|
||||
glyph_t glyph) {
|
||||
qreal rb;
|
||||
engine->getGlyphBearings(glyph, 0, &rb);
|
||||
|
||||
// We only care about negative right bearings, so we limit the range
|
||||
// of the bearing here so that we can assume it's negative in the rest
|
||||
// of the code, as well ase use QFixed(1) as a sentinel to represent
|
||||
// the state where we have yet to compute the right bearing.
|
||||
rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
|
||||
}
|
||||
|
||||
void LineBreakHelper::calculateRightBearing() {
|
||||
if (currentPosition > 0
|
||||
&& logClusters[currentPosition - 1] < glyphs.numGlyphs) {
|
||||
calculateRightBearing(fontEngine, currentGlyph());
|
||||
} else {
|
||||
rightBearing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBreakHelper::calculateRightBearingForPreviousGlyph() {
|
||||
if (previousGlyph_ > 0) {
|
||||
calculateRightBearing(previousFontEngine_, previousGlyph_);
|
||||
} else {
|
||||
rightBearing = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// We always calculate the right bearing right before it is needed.
|
||||
// So we don't need caching / optimizations referred to delayed right bearing calculations.
|
||||
|
||||
//static const QFixed RightBearingNotCalculated;
|
||||
|
||||
//inline void resetRightBearing()
|
||||
//{
|
||||
// rightBearing = RightBearingNotCalculated;
|
||||
//}
|
||||
|
||||
// We express the negative right bearing as an absolute number
|
||||
// so that it can be applied to the width using addition.
|
||||
QFixed LineBreakHelper::negativeRightBearing() const {
|
||||
//if (rightBearing == RightBearingNotCalculated)
|
||||
// return QFixed(0);
|
||||
|
||||
return qAbs(rightBearing);
|
||||
}
|
||||
|
||||
QString DebugCurrentParsingString, DebugCurrentParsingPart;
|
||||
int DebugCurrentParsingFrom = 0;
|
||||
int DebugCurrentParsingLength = 0;
|
||||
|
||||
static inline void addNextCluster(int &pos, int end, ScriptLine &line, int &glyphCount,
|
||||
const QScriptItem ¤t, const unsigned short *logClusters,
|
||||
const QGlyphLayout &glyphs)
|
||||
{
|
||||
void addNextCluster(
|
||||
int &pos,
|
||||
int end,
|
||||
ScriptLine &line,
|
||||
int &glyphCount,
|
||||
const QScriptItem ¤t,
|
||||
const unsigned short *logClusters,
|
||||
const QGlyphLayout &glyphs) {
|
||||
int glyphPosition = logClusters[pos];
|
||||
do { // got to the first next cluster
|
||||
++pos;
|
||||
|
|
@ -142,7 +153,8 @@ static inline void addNextCluster(int &pos, int end, ScriptLine &line, int &glyp
|
|||
if (!glyphs.attributes[glyphPosition].dontPrint)
|
||||
line.textWidth += glyphs.advances[glyphPosition];
|
||||
++glyphPosition;
|
||||
} while (glyphPosition < current.num_glyphs && !glyphs.attributes[glyphPosition].clusterStart);
|
||||
} while (glyphPosition < current.num_glyphs
|
||||
&& !glyphs.attributes[glyphPosition].clusterStart);
|
||||
|
||||
if (!((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition)) {
|
||||
auto str = QStringList();
|
||||
|
|
@ -153,7 +165,8 @@ static inline void addNextCluster(int &pos, int end, ScriptLine &line, int &glyp
|
|||
base::Integration::Instance().logAssertionViolation(QString("pos: %1, end: %2, glyphPosition: %3, glyphCount: %4, lineLength: %5, num_glyphs: %6, logClusters[0..pos]: %7").arg(pos).arg(end).arg(glyphPosition).arg(glyphCount).arg(line.length).arg(current.num_glyphs).arg(str.join(",")));
|
||||
Unexpected("Values in addNextCluster()");
|
||||
}
|
||||
Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) || logClusters[pos] == glyphPosition);
|
||||
Q_ASSERT((pos == end && glyphPosition == current.num_glyphs)
|
||||
|| logClusters[pos] == glyphPosition);
|
||||
|
||||
++glyphCount;
|
||||
}
|
||||
|
|
@ -162,13 +175,36 @@ static inline void addNextCluster(int &pos, int end, ScriptLine &line, int &glyp
|
|||
|
||||
class BlockParser {
|
||||
public:
|
||||
BlockParser(
|
||||
QTextEngine *e,
|
||||
TextBlock *b,
|
||||
QFixed minResizeWidth,
|
||||
int blockFrom,
|
||||
const QString &str);
|
||||
|
||||
BlockParser(QTextEngine *e, TextBlock *b, QFixed minResizeWidth, int32 blockFrom, const QString &str)
|
||||
: block(b), eng(e), str(str) {
|
||||
private:
|
||||
void parseWords(QFixed minResizeWidth, int blockFrom);
|
||||
bool isLineBreak(const QCharAttributes *attributes, int index);
|
||||
|
||||
TextBlock *block;
|
||||
QTextEngine *eng;
|
||||
const QString &str;
|
||||
|
||||
};
|
||||
|
||||
BlockParser::BlockParser(
|
||||
QTextEngine *e,
|
||||
TextBlock *b,
|
||||
QFixed minResizeWidth,
|
||||
int blockFrom,
|
||||
const QString &str)
|
||||
: block(b)
|
||||
, eng(e)
|
||||
, str(str) {
|
||||
parseWords(minResizeWidth, blockFrom);
|
||||
}
|
||||
}
|
||||
|
||||
void parseWords(QFixed minResizeWidth, int32 blockFrom) {
|
||||
void BlockParser::parseWords(QFixed minResizeWidth, int blockFrom) {
|
||||
LineBreakHelper lbh;
|
||||
|
||||
// Helper for debugging crashes in text processing.
|
||||
|
|
@ -291,23 +327,20 @@ public:
|
|||
block->_width -= block->_rpadding;
|
||||
block->_words.squeeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isLineBreak(const QCharAttributes *attributes, int32 index) {
|
||||
bool BlockParser::isLineBreak(
|
||||
const QCharAttributes *attributes,
|
||||
int index) {
|
||||
bool lineBreak = attributes[index].lineBreak;
|
||||
if (lineBreak && block->lnkIndex() > 0 && index > 0 && str.at(index - 1) == '/') {
|
||||
if (lineBreak
|
||||
&& block->lnkIndex() > 0
|
||||
&& index > 0
|
||||
&& str.at(index - 1) == '/') {
|
||||
return false; // don't break after / in links
|
||||
}
|
||||
return lineBreak;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TextBlock *block;
|
||||
QTextEngine *eng;
|
||||
const QString &str;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
QFixed AbstractBlock::f_rbearing() const {
|
||||
return (type() == TextBlockTText)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
namespace TextUtilities {
|
||||
namespace {
|
||||
|
||||
using namespace Ui::Text;
|
||||
|
||||
QString ExpressionMailNameAtEnd() {
|
||||
// Matches email first part (before '@') at the end of the string.
|
||||
// First we find a domain without protocol (like "gmail.com"), then
|
||||
|
|
@ -1189,6 +1191,18 @@ std::unique_ptr<QMimeData> MimeDataFromText(
|
|||
return result;
|
||||
}
|
||||
|
||||
bool IsSentencePartEnd(QChar ch) {
|
||||
return (ch == ',')
|
||||
|| (ch == ':')
|
||||
|| (ch == ';');
|
||||
}
|
||||
|
||||
bool IsSentenceEnd(QChar ch) {
|
||||
return (ch == '.')
|
||||
|| (ch == '?')
|
||||
|| (ch == '!');
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const QRegularExpression &RegExpMailNameAtEnd() {
|
||||
|
|
@ -1303,10 +1317,10 @@ QString SingleLine(const QString &text) {
|
|||
auto s = text.unicode(), ch = s, e = text.unicode() + text.size();
|
||||
|
||||
// Trim.
|
||||
while (s < e && chIsTrimmed(*s)) {
|
||||
while (s < e && IsTrimmed(*s)) {
|
||||
++s;
|
||||
}
|
||||
while (s < e && chIsTrimmed(*(e - 1))) {
|
||||
while (s < e && IsTrimmed(*(e - 1))) {
|
||||
--e;
|
||||
}
|
||||
if (e - s != text.size()) {
|
||||
|
|
@ -1314,7 +1328,7 @@ QString SingleLine(const QString &text) {
|
|||
}
|
||||
|
||||
for (auto ch = s; ch != e; ++ch) {
|
||||
if (chIsNewline(*ch) || *ch == TextCommand) {
|
||||
if (IsNewline(*ch) || *ch == TextCommand) {
|
||||
result[int(ch - s)] = QChar::Space;
|
||||
}
|
||||
}
|
||||
|
|
@ -1336,7 +1350,7 @@ QString RemoveAccents(const QString &text) {
|
|||
if (copying) result[i] = *ch;
|
||||
continue;
|
||||
}
|
||||
if (chIsDiac(*ch)) {
|
||||
if (IsDiac(*ch)) {
|
||||
copying = true;
|
||||
--i;
|
||||
continue;
|
||||
|
|
@ -1434,14 +1448,14 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
|
|||
if (inEntity && !canBreakEntity) {
|
||||
markGoodAsLevel(0);
|
||||
} else {
|
||||
if (chIsNewline(*ch)) {
|
||||
if (IsNewline(*ch)) {
|
||||
if (inEntity) {
|
||||
if (ch + 1 < end && chIsNewline(*(ch + 1))) {
|
||||
if (ch + 1 < end && IsNewline(*(ch + 1))) {
|
||||
markGoodAsLevel(12);
|
||||
} else {
|
||||
markGoodAsLevel(11);
|
||||
}
|
||||
} else if (ch + 1 < end && chIsNewline(*(ch + 1))) {
|
||||
} else if (ch + 1 < end && IsNewline(*(ch + 1))) {
|
||||
markGoodAsLevel(15);
|
||||
} else if (currentEntity < entityCount && ch + 1 == start + left.entities[currentEntity].offset() && left.entities[currentEntity].type() == EntityType::Pre) {
|
||||
markGoodAsLevel(14);
|
||||
|
|
@ -1450,15 +1464,15 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
|
|||
} else {
|
||||
markGoodAsLevel(13);
|
||||
}
|
||||
} else if (chIsSpace(*ch)) {
|
||||
if (chIsSentenceEnd(*(ch - 1))) {
|
||||
} else if (IsSpace(*ch)) {
|
||||
if (IsSentenceEnd(*(ch - 1))) {
|
||||
markGoodAsLevel(9 + noEntityLevel);
|
||||
} else if (chIsSentencePartEnd(*(ch - 1))) {
|
||||
} else if (IsSentencePartEnd(*(ch - 1))) {
|
||||
markGoodAsLevel(7 + noEntityLevel);
|
||||
} else {
|
||||
markGoodAsLevel(5 + noEntityLevel);
|
||||
}
|
||||
} else if (chIsWordSeparator(*(ch - 1))) {
|
||||
} else if (IsWordSeparator(*(ch - 1))) {
|
||||
markGoodAsLevel(3 + noEntityLevel);
|
||||
} else {
|
||||
markGoodAsLevel(1 + noEntityLevel);
|
||||
|
|
@ -1761,13 +1775,14 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
|||
const QChar *domainEnd = start + mDomain.capturedEnd(), *p = domainEnd;
|
||||
for (; p < end; ++p) {
|
||||
QChar ch(*p);
|
||||
if (chIsLinkEnd(ch)) break; // link finished
|
||||
if (chIsAlmostLinkEnd(ch)) {
|
||||
if (IsLinkEnd(ch)) {
|
||||
break; // link finished
|
||||
} else if (IsAlmostLinkEnd(ch)) {
|
||||
const QChar *endTest = p + 1;
|
||||
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
|
||||
while (endTest < end && IsAlmostLinkEnd(*endTest)) {
|
||||
++endTest;
|
||||
}
|
||||
if (endTest >= end || chIsLinkEnd(*endTest)) {
|
||||
if (endTest >= end || IsLinkEnd(*endTest)) {
|
||||
break; // link finished at p
|
||||
}
|
||||
p = endTest;
|
||||
|
|
@ -1879,7 +1894,7 @@ void ApplyServerCleaning(TextWithEntities &result) {
|
|||
if (ch->unicode() == '\r') {
|
||||
MovePartAndGoForward(result, to, from, (ch - start) - from);
|
||||
++from;
|
||||
} else if (chReplacedBySpace(*ch)) {
|
||||
} else if (IsReplacedBySpace(*ch)) {
|
||||
*ch = ' ';
|
||||
}
|
||||
}
|
||||
|
|
@ -1893,7 +1908,7 @@ void Trim(TextWithEntities &result) {
|
|||
// right trim
|
||||
for (auto s = result.text.data(), e = s + result.text.size(), ch = e; ch != s;) {
|
||||
--ch;
|
||||
if (!chIsTrimmed(*ch)) {
|
||||
if (!IsTrimmed(*ch)) {
|
||||
if (ch + 1 < e) {
|
||||
auto l = ch + 1 - s;
|
||||
for (auto &entity : result.entities) {
|
||||
|
|
@ -1916,7 +1931,7 @@ void Trim(TextWithEntities &result) {
|
|||
|
||||
// left trim
|
||||
for (auto s = result.text.data(), ch = s, e = s + result.text.size(); ch != e; ++ch) {
|
||||
if (!chIsTrimmed(*ch) || (ch - s) == firstMonospaceOffset) {
|
||||
if (!IsTrimmed(*ch) || (ch - s) == firstMonospaceOffset) {
|
||||
if (ch > s) {
|
||||
auto l = ch - s;
|
||||
for (auto &entity : result.entities) {
|
||||
|
|
|
|||
|
|
@ -104,12 +104,12 @@ QString GetFullSimpleTextTag(const TextWithTags &textWithTags) {
|
|||
auto from = 0;
|
||||
auto till = int(text.size());
|
||||
for (; from != till; ++from) {
|
||||
if (!IsNewline(text[from]) && !chIsSpace(text[from])) {
|
||||
if (!IsNewline(text[from]) && !Text::IsSpace(text[from])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (till != from) {
|
||||
if (!IsNewline(text[till - 1]) && !chIsSpace(text[till - 1])) {
|
||||
if (!IsNewline(text[till - 1]) && !Text::IsSpace(text[till - 1])) {
|
||||
break;
|
||||
}
|
||||
--till;
|
||||
|
|
@ -3100,7 +3100,7 @@ bool InputField::commitMarkdownReplacement(
|
|||
const auto ch = outer.at(check);
|
||||
if (IsNewline(ch)) {
|
||||
return check + 1;
|
||||
} else if (!chIsSpace(ch)) {
|
||||
} else if (!Text::IsSpace(ch)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue