diff --git a/libs/qsynedit/qsynedit/document.cpp b/libs/qsynedit/qsynedit/document.cpp index 1b2c0d9b..35284a7a 100644 --- a/libs/qsynedit/qsynedit/document.cpp +++ b/libs/qsynedit/qsynedit/document.cpp @@ -25,27 +25,24 @@ #include #include #include "qt_utils/charsetinfo.h" +#include #include namespace QSynedit { Document::Document(const QFont& font, QObject *parent): QObject{parent}, - mFontMetrics{font}, - mTabSize{4}, - mForceMonospace{false}, mSetLineWidthLockCount{0}, mMaxLineChangedInSetLinesWidth{false}, - mMutex{} + mMutex{}, + mGlyphCalculator{font} { mAppendNewLineAtEOF = true; mNewlineType = NewlineType::Windows; mIndexOfLongestLine = -1; mUpdateCount = 0; - mCharWidth = mFontMetrics.horizontalAdvance("M"); - mSpaceWidth = mFontMetrics.horizontalAdvance(" "); - mUpdateDocumentLineWidthFunc = std::bind(&Document::calcLineWidth, - this, + mUpdateDocumentLineWidthFunc = std::bind(&GlyphCalculator::calcLineWidth, + &mGlyphCalculator, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); @@ -95,6 +92,22 @@ int Document::lineWidth(int line) return 0; } +void Document::updateLineWidth(int line) +{ + QMutexLocker locker(&mMutex); + if (line>=0 && line < mLines.size()) { + if (mLines[line]->mWidth<0) { + int width; + QList glyphPositions = mGlyphCalculator.calcLineWidth( + mLines[line]->lineText(), + mLines[line]->glyphStartCharList(), + width); + setLineWidth(line, width,glyphPositions); + mLines[line]->mIsTempWidth = true; + } + } +} + int Document::lineWidth(int line, const QString &newText) { QMutexLocker locker(&mMutex); @@ -104,7 +117,7 @@ int Document::lineWidth(int line, const QString &newText) if (lineText==newText) { return mLines[line]->width(); } else { - return stringWidth(newText,0); + return mGlyphCalculator.stringWidth(newText,0); } } @@ -571,40 +584,22 @@ void Document::saveUTF32File(QFile &file, QTextCodec* codec) file.write(codec->fromUnicode(text)); } -bool Document::forceMonospace() const +void Document::setTabSize(int newTabSize) { - return mForceMonospace; + if (tabSize()!=newTabSize) { + mGlyphCalculator.setTabSize(newTabSize); + invalidateAllLineWidth(); + } } void Document::setForceMonospace(bool newForceMonospace) { - int oldValue = mForceMonospace; - mForceMonospace = newForceMonospace; - if (oldValue != mForceMonospace) + int oldValue = forceMonospace(); + mGlyphCalculator.setForceMonospace(newForceMonospace); + if (oldValue != newForceMonospace) invalidateAllLineWidth(); } -const QFontMetrics &Document::fontMetrics() const -{ - return mFontMetrics; -} - -void Document::setFont(const QFont &newFont) -{ - mFontMetrics = QFontMetrics(newFont); - mCharWidth = mFontMetrics.horizontalAdvance("M"); - mSpaceWidth = mFontMetrics.horizontalAdvance(" "); - invalidateAllLineWidth(); -} - -void Document::setTabSize(int newTabSize) -{ - if (mTabSize!=newTabSize) { - mTabSize = newTabSize; - invalidateAllLineWidth(); - } -} - void Document::loadFromFile(const QString& filename, const QByteArray& encoding, QByteArray& realEncoding) { QMutexLocker locker(&mMutex); @@ -888,7 +883,7 @@ QList calcGlyphStartCharList(const QString &text) return glyphStartCharList; } -int Document::stringWidth(const QString &str, int left) const +int GlyphCalculator::stringWidth(const QString &str, int left) const { QList glyphStartCharList = calcGlyphStartCharList(str); int right; @@ -896,7 +891,7 @@ int Document::stringWidth(const QString &str, int left) const return right - left; } -int Document::stringWidth(const QString &str, int left, const QFontMetrics &fontMetrics) +int GlyphCalculator::stringWidth(const QString &str, int left, const QFontMetrics &fontMetrics) { QList glyphStartCharList = calcGlyphStartCharList(str); int right; @@ -944,11 +939,6 @@ int Document::glyphWidth(int line, int glyphIdx) return mLines[line]->glyphWidth(glyphIdx); } -int Document::glyphWidth(const QString &glyph, int left) const -{ - return glyphWidth(glyph,left,mFontMetrics,mForceMonospace); -} - int Document::charToGlyphIndex(int line, int charIdx) { QMutexLocker locker(&mMutex); @@ -974,12 +964,7 @@ int Document::charToGlyphIndex(const QString& str, QList glyphStartCharList // return glyphStartCharList.length()-1; } -QList Document::calcLineWidth(const QString &lineText, const QList &glyphStartCharList, int &width) -{ - return calcGlyphPositionList(lineText,glyphStartCharList,0,width); -} - -QList Document::calcGlyphPositionList(const QString &lineText, const QList &glyphStartCharList, const QFontMetrics &fontMetrics, int left, int &right) const +QList GlyphCalculator::calcGlyphPositionList(const QString &lineText, const QList &glyphStartCharList, const QFontMetrics &fontMetrics, int left, int &right) const { right = std::max(0,left); int start,end; @@ -1050,7 +1035,7 @@ int Document::charToGlyphStartPosition(int line, const QString newStr, int charP QList glyphStartCharList = calcGlyphStartCharList(newStr); int glyphIdx = charToGlyphIndex(newStr, glyphStartCharList, charPos); int width; - QList glyphStartPositionList = calcGlyphPositionList(newStr, width); + QList glyphStartPositionList = mGlyphCalculator.calcGlyphPositionList(newStr, width); if (glyphIdxglyphStartPositionList(); width = mLines[line]->width(); } else { - glyphPositionList = calcGlyphPositionList(mLines[line]->lineText(), width); + glyphPositionList = mGlyphCalculator.calcGlyphPositionList(mLines[line]->lineText(), width); } int glyphIdx = xposToGlyphIndex(width, glyphPositionList, xpos); return mLines[line]->glyphStartChar(glyphIdx); @@ -1206,7 +1191,8 @@ void Document::updateMaxLineWidthChanged() if (mSetLineWidthLockCount>0) { mMaxLineChangedInSetLinesWidth = true; } else { - updateMaxLineWidthAndNotify(); + //updateMaxLineWidthAndNotify(); + emit maxLineWidthChanged(); } } @@ -1227,15 +1213,7 @@ void Document::updateMaxLineWidthAndNotify() emit maxLineWidthChanged(); } -QList Document::calcGlyphPositionList(const QString &lineText, const QList &glyphStartCharList, int left, int &right) const -{ - return calcGlyphPositionList(lineText, glyphStartCharList, - mFontMetrics, - left,right); -} - - -QList Document::calcGlyphPositionList(const QString &lineText, int &width) const +QList GlyphCalculator::calcGlyphPositionList(const QString &lineText, int &width) const { QList glyphStartCharList = calcGlyphStartCharList(lineText); return calcGlyphPositionList(lineText,glyphStartCharList,0,width); @@ -1290,13 +1268,20 @@ void Document::invalidateAllLineWidth() line->invalidateWidth(); } mIndexOfLongestLine = -1; + FindMaxLineWidthThread *thread = new FindMaxLineWidthThread(mLines, mGlyphCalculator); + connect(thread, &FindMaxLineWidthThread::maxWidthLineFound, + this, &Document::maxWidthLineFound); + connect(thread, &QThread::finished, + thread, &QThread::deleteLater); + thread->start(); } DocumentLine::DocumentLine(DocumentLine::UpdateWidthFunc updateWidthFunc): mSyntaxState{}, mWidth{-1}, mIsTempWidth{true}, - mUpdateWidthFunc{updateWidthFunc} + mUpdateWidthFunc{updateWidthFunc}, + mTimestamp{QDateTime::currentMSecsSinceEpoch()} { } @@ -1339,6 +1324,7 @@ int DocumentLine::width() void DocumentLine::setLineText(const QString &newLineText) { + mTimestamp = QDateTime::currentMSecsSinceEpoch(); mLineText = newLineText; mGlyphStartCharList = calcGlyphStartCharList(newLineText); invalidateWidth(); @@ -1697,7 +1683,7 @@ int searchForSegmentIdx(const QList &segList, int minVal, int maxVal, int v return -1; } -int Document::updateGlyphStartPositionList( +int GlyphCalculator::updateGlyphStartPositionList( const QString &lineText, const QList &glyphStartCharList, int startChar, int endChar, const QFontMetrics &fontMetrics, @@ -1724,7 +1710,7 @@ int Document::updateGlyphStartPositionList( return right-left; } -int Document::glyphWidth(const QString &glyph, int left, const QFontMetrics &fontMetrics, bool forceMonospace) const +int GlyphCalculator::glyphWidth(const QString &glyph, int left, const QFontMetrics &fontMetrics, bool forceMonospace) const { int glyphWidth; if (glyph.length()==0) @@ -1769,4 +1755,45 @@ int segmentIntervalStart(const QList &segList, int minVal, int maxVal, int return segList[idx]; } +GlyphCalculator::GlyphCalculator(const QFont &font): + mFontMetrics{font}, + mTabSize{4}, + mForceMonospace{false} +{ + mCharWidth = mFontMetrics.horizontalAdvance("M"); + mSpaceWidth = mFontMetrics.horizontalAdvance(" "); +} + +void GlyphCalculator::setFont(const QFont &newFont) +{ + mFontMetrics = QFontMetrics(newFont); + mCharWidth = mFontMetrics.horizontalAdvance("M"); + mSpaceWidth = mFontMetrics.horizontalAdvance(" "); +} + +FindMaxLineWidthThread::FindMaxLineWidthThread(const DocumentLines &lines, const GlyphCalculator &glyphCalculator, QObject *parent): + QThread(parent), + mLines{lines}, + mGlyphCalculator{glyphCalculator} +{ + +} + +void FindMaxLineWidthThread::run() +{ + int maxWidth = 0; + int maxWidthLine = -1; + for (int i=0;imWidth; + if ( width < 0 ) + width = mGlyphCalculator.stringWidth(mLines[i]->lineText(),0); + if (width > maxWidth) { + maxWidth = width; + maxWidthLine = i; + } + } + if (maxWidthLine >= 0) + emit maxWidthLineFound(maxWidthLine); +} + } diff --git a/libs/qsynedit/qsynedit/document.h b/libs/qsynedit/qsynedit/document.h index 7f549ac4..f145601e 100644 --- a/libs/qsynedit/qsynedit/document.h +++ b/libs/qsynedit/qsynedit/document.h @@ -36,6 +36,7 @@ QList calcGlyphStartCharList(const QString &text); void expandGlyphStartCharList(const QString& strAdded, int oldStrLen, QList &glyphStartCharList); class Document; +class FindMaxLineWidthThread; using SearchConfirmAroundProc = std::function; /** @@ -178,8 +179,9 @@ private: int mWidth; bool mIsTempWidth; UpdateWidthFunc mUpdateWidthFunc; - + qint64 mTimestamp; friend class Document; + friend class FindMaxLineWidthThread; }; typedef std::shared_ptr PDocumentLine; @@ -195,6 +197,105 @@ public: explicit BinaryFileError (const QString& reason); }; +class GlyphCalculator { +public: + explicit GlyphCalculator(const QFont& font); + + int tabSize() const { + return mTabSize; + } + void setTabSize(int newTabSize) { mTabSize = newTabSize; } + + + int tabWidth() const { + return mTabSize * spaceWidth(); + } + + int spaceWidth() const { + if (mForceMonospace) + return mCharWidth; + return mSpaceWidth; + } + + bool forceMonospace() const { return mForceMonospace; } + + void setForceMonospace(bool newForceMonospace) { mForceMonospace = newForceMonospace; } + + const QFontMetrics &fontMetrics() const { return mFontMetrics; } + + void setFont(const QFont &newFont); + + int glyphWidth(const QString& glyph, int left, + const QFontMetrics &fontMetrics, + bool forceMonospace) const; + + int glyphWidth(const QString &glyph, int left) const{ + return glyphWidth(glyph,left,mFontMetrics,mForceMonospace); + } + + QList calcGlyphPositionList(const QString& lineText, const QList &glyphStartCharList, + const QFontMetrics &fontMetrics, + int left, int &right) const; + + QList calcGlyphPositionList(const QString& lineText, const QList &glyphStartCharList, int left, int &right) const { + return calcGlyphPositionList(lineText, glyphStartCharList, + mFontMetrics, + left,right); + } + + /** + * @brief calculate display width of a string + * + * The string may contains the tab char, whose width depends on the tab size and it's position + * + * @param str the string to be displayed + * @param left start x pos of the string + * @return width of the string, don't including colsBefore + */ + int stringWidth(const QString &str, int left) const; + + int stringWidth(const QString &str, int left, const QFontMetrics &fontMetrics); + + QList calcLineWidth(const QString& lineText, const QList &glyphStartCharList, int &width) { + return calcGlyphPositionList(lineText,glyphStartCharList,0,width); + } + QList calcGlyphPositionList(const QString& lineText, int &width) const; + + int updateGlyphStartPositionList( + const QString& lineText, + const QList &glyphStartCharList, + int startChar, int endChar, + const QFontMetrics &fontMetrics, + QList &glyphStartPositionList, + int left, int &right, int &startGlyph, int &endGlyph) const; +private: + QFontMetrics mFontMetrics; + int mTabSize; + int mCharWidth; + int mSpaceWidth; + bool mForceMonospace; +}; + +class FindMaxLineWidthThread: public QThread { + Q_OBJECT +public: + explicit FindMaxLineWidthThread( + const DocumentLines &lines, + const GlyphCalculator& glyphCalculator, + QObject* parent=nullptr); + FindMaxLineWidthThread(const FindMaxLineWidthThread&) = delete; +signals: + void maxWidthLineFound(int line); +private: + DocumentLines mLines; + GlyphCalculator mGlyphCalculator; + + // QThread interface +protected: + void run() override; +}; + + /** * @brief The Document class * @@ -248,6 +349,8 @@ public: * @return */ int lineWidth(int line); + + void updateLineWidth(int line); /** * @brief get width of the specified text / line @@ -425,20 +528,12 @@ public: QString glyph(int line, int glyphIdx); QString glyphAt(int line, int charPos); + int stringWidth(const QString &str, int left) const { + return mGlyphCalculator.stringWidth(str, left); + } + int charToGlyphStartChar(int line, int charPos); //int columnToGlyphStartColumn(int line, int charPos); - /** - * @brief calculate display width of a string - * - * The string may contains the tab char, whose width depends on the tab size and it's position - * - * @param str the string to be displayed - * @param left start x pos of the string - * @return width of the string, don't including colsBefore - */ - int stringWidth(const QString &str, int left) const; - - int stringWidth(const QString &str, int left, const QFontMetrics &fontMetrics); int glyphCount(int line); /** @@ -485,7 +580,9 @@ public: */ int glyphWidth(int line, int glyphIdx); - int glyphWidth(const QString &glyph, int left) const; + int glyphWidth(const QString &glyph, int left) const { + return mGlyphCalculator.glyphWidth(glyph,left); + } /** * @brief get index of the glyph represented by the specified char @@ -514,14 +611,6 @@ public: int charToGlyphStartPosition(int line, const QString newStr, int charPos); int xposToGlyphStartChar(int line, const QString newStr, int xpos); - int updateGlyphStartPositionList( - const QString& lineText, - const QList &glyphStartCharList, - int startChar, int endChar, - const QFontMetrics &fontMetrics, - QList &glyphStartPositionList, - int left, int &right, int &startGlyph, int &endGlyph) const; - bool getAppendNewLineAtEOF(); void setAppendNewLineAtEOF(bool appendNewLineAtEOF); @@ -530,26 +619,21 @@ public: bool empty(); - int tabSize() const { - return mTabSize; - } + int tabSize() const { return mGlyphCalculator.tabSize(); } - int tabWidth() const { - return mTabSize * spaceWidth(); - } + int tabWidth() const { return mGlyphCalculator.tabWidth(); } - int spaceWidth() const { - if (mForceMonospace) - return mCharWidth; - return mSpaceWidth; - } + int spaceWidth() const { return mGlyphCalculator.spaceWidth(); } void setTabSize(int newTabSize); - const QFontMetrics &fontMetrics() const; - void setFont(const QFont &newFont); + const QFontMetrics &fontMetrics() const { return mGlyphCalculator.fontMetrics(); } + void setFont(const QFont &newFont) { + mGlyphCalculator.setFont(newFont); + invalidateAllLineWidth(); + } - bool forceMonospace() const; + bool forceMonospace() const { return mGlyphCalculator.forceMonospace(); } void setForceMonospace(bool newForceMonospace); public slots: @@ -563,6 +647,7 @@ signals: void inserted(int startLine, int count); void putted(int line); void maxLineWidthChanged(); + void lineWidthUpdateNeeded(int line); protected: QString getTextStr() const; void setUpdateState(bool Updating); @@ -570,6 +655,9 @@ protected: void addItem(const QString& s); void putTextStr(const QString& text); void internalClear(); + void maxWidthLineFound(int line) { + emit lineWidthUpdateNeeded(line); + } private: bool lineWidthValid(int line); void beginSetLinesWidth(); @@ -578,18 +666,8 @@ private: void updateMaxLineWidthChanged(); void updateMaxLineWidthAndNotify(); - int glyphWidth(const QString& glyph, int left, - const QFontMetrics &fontMetrics, - bool forceMonospace) const; - int xposToGlyphIndex(int strWidth, QList glyphPositionList, int xpos) const; int charToGlyphIndex(const QString& str, QList glyphStartCharList, int charPos) const; - QList calcLineWidth(const QString& lineText, const QList &glyphStartCharList, int &width); - QList calcGlyphPositionList(const QString& lineText, const QList &glyphStartCharList, - const QFontMetrics &fontMetrics, - int left, int &right) const; - QList calcGlyphPositionList(const QString& lineText, const QList &glyphStartCharList, int left, int &right) const; - QList calcGlyphPositionList(const QString& lineText, int &width) const; QList getGlyphStartCharList(int line, const QString &lineText); QList getGlyphStartCharList(int line); QList getGlyphStartPositionList(int line); @@ -605,24 +683,17 @@ private: DocumentLine::UpdateWidthFunc mUpdateDocumentLineWidthFunc; - //SynEdit* mEdit; - - QFontMetrics mFontMetrics; - int mTabSize; - int mCharWidth; - int mSpaceWidth; - //int mCount; - //int mCapacity; NewlineType mNewlineType; bool mAppendNewLineAtEOF; int mIndexOfLongestLine; int mUpdateCount; - bool mForceMonospace; int mSetLineWidthLockCount; bool mMaxLineChangedInSetLinesWidth; QRecursiveMutex mMutex; + GlyphCalculator mGlyphCalculator; + friend class QSynEditPainter; }; diff --git a/libs/qsynedit/qsynedit/painter.cpp b/libs/qsynedit/qsynedit/painter.cpp index 97421f8e..761e0b94 100644 --- a/libs/qsynedit/qsynedit/painter.cpp +++ b/libs/qsynedit/qsynedit/painter.cpp @@ -750,7 +750,6 @@ void QSynEditPainter::addHighlightToken( } if (mIsSpecialLine) { - QColor oldForeground = foreground; if (mSpecialLineForeground.isValid()) foreground = mSpecialLineForeground; if (mSpecialLineBackground.isValid()) @@ -788,7 +787,7 @@ void QSynEditPainter::addHighlightToken( } //calculate width of the token ( and update it's glyph start positions ) if (calcGlyphPosition) { - tokenWidth = mEdit->mDocument->updateGlyphStartPositionList( + tokenWidth = mEdit->mDocument->mGlyphCalculator.updateGlyphStartPositionList( lineText, glyphStartCharList, tokenStartChar, diff --git a/libs/qsynedit/qsynedit/qsynedit.cpp b/libs/qsynedit/qsynedit/qsynedit.cpp index a6ed8e45..4a16ac96 100644 --- a/libs/qsynedit/qsynedit/qsynedit.cpp +++ b/libs/qsynedit/qsynedit/qsynedit.cpp @@ -44,6 +44,20 @@ #define UPDATE_HORIZONTAL_SCROLLBAR_EVENT ((QEvent::Type)(QEvent::User+1)) #define UPDATE_VERTICAL_SCROLLBAR_EVENT ((QEvent::Type)(QEvent::User+2)) +#define UPDATE_LINE_WIDTH_EVENT ((QEvent::Type)(QEvent::User+3)) + +class UpdateLineWidthEvent: public QEvent{ +public: + explicit UpdateLineWidthEvent(int line): + QEvent{UPDATE_LINE_WIDTH_EVENT}, + mLine{line} + { + + } + int line() const { return mLine; } +private: + int mLine; +}; namespace QSynedit { QSynEdit::QSynEdit(QWidget *parent) : QAbstractScrollArea(parent), @@ -77,6 +91,7 @@ QSynEdit::QSynEdit(QWidget *parent) : QAbstractScrollArea(parent), connect(mDocument.get(), &Document::cleared, this, &QSynEdit::updateVScrollbar); connect(mDocument.get(), &Document::deleted, this, &QSynEdit::updateVScrollbar); connect(mDocument.get(), &Document::inserted, this, &QSynEdit::updateVScrollbar); + connect(mDocument.get(), &Document::lineWidthUpdateNeeded, this, &QSynEdit::onLineWidthUpdateNeeded); mGutterWidth = 0; @@ -1755,6 +1770,12 @@ void QSynEdit::onMaxLineWidthChanged() updateHScrollBarLater(); } +void QSynEdit::onLineWidthUpdateNeeded(int line) +{ + UpdateLineWidthEvent * event = new UpdateLineWidthEvent(line); + qApp->postEvent(this,event); +} + void QSynEdit::updateHScrollBarLater() { QEvent * event = new QEvent(UPDATE_HORIZONTAL_SCROLLBAR_EVENT); @@ -3143,6 +3164,11 @@ void QSynEdit::doUpdateVScrollbar() verticalScrollBar()->setSingleStep(mTextHeight); } +void QSynEdit::doUpdateLineWidth(int line) +{ + mDocument->updateLineWidth(line); +} + void QSynEdit::updateCaret() { @@ -5979,10 +6005,17 @@ bool QSynEdit::event(QEvent *event) { switch(event->type()) { case UPDATE_HORIZONTAL_SCROLLBAR_EVENT: + event->setAccepted(true); doUpdateHScrollbar(); break; + case UPDATE_LINE_WIDTH_EVENT: { + event->setAccepted(true); + UpdateLineWidthEvent* updateEvent = dynamic_cast(event); + doUpdateLineWidth(updateEvent->line()); + } + break; case QEvent::KeyPress:{ - QKeyEvent* keyEvent = static_cast(event); + QKeyEvent* keyEvent = dynamic_cast(event); if(keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) { // process tab key presse event diff --git a/libs/qsynedit/qsynedit/qsynedit.h b/libs/qsynedit/qsynedit/qsynedit.h index 34627d7b..4c592446 100644 --- a/libs/qsynedit/qsynedit/qsynedit.h +++ b/libs/qsynedit/qsynedit/qsynedit.h @@ -525,6 +525,7 @@ private: void doUpdateHScrollbar(); void updateVScrollbar(); void doUpdateVScrollbar(); + void doUpdateLineWidth(int line); void updateCaret(); void recalcCharExtent(); QString expandAtWideGlyphs(const QString& S); @@ -645,6 +646,7 @@ private: private slots: void onMaxLineWidthChanged(); + void onLineWidthUpdateNeeded(int line); void updateHScrollBarLater(); void onBookMarkOptionsChanged(); void onGutterChanged();