update max line width in background

This commit is contained in:
Roy Qu 2024-05-07 14:41:04 +08:00
parent 8faa2c0aa6
commit 8fef396baa
5 changed files with 253 additions and 121 deletions

View File

@ -25,27 +25,24 @@
#include <QMessageBox>
#include <cmath>
#include "qt_utils/charsetinfo.h"
#include <QDateTime>
#include <QDebug>
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<int> 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<int> 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<int> 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<int> 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<int> glyphStartCharList
// return glyphStartCharList.length()-1;
}
QList<int> Document::calcLineWidth(const QString &lineText, const QList<int> &glyphStartCharList, int &width)
{
return calcGlyphPositionList(lineText,glyphStartCharList,0,width);
}
QList<int> Document::calcGlyphPositionList(const QString &lineText, const QList<int> &glyphStartCharList, const QFontMetrics &fontMetrics, int left, int &right) const
QList<int> GlyphCalculator::calcGlyphPositionList(const QString &lineText, const QList<int> &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<int> glyphStartCharList = calcGlyphStartCharList(newStr);
int glyphIdx = charToGlyphIndex(newStr, glyphStartCharList, charPos);
int width;
QList<int> glyphStartPositionList = calcGlyphPositionList(newStr, width);
QList<int> glyphStartPositionList = mGlyphCalculator.calcGlyphPositionList(newStr, width);
if (glyphIdx<glyphStartCharList.length())
return glyphStartPositionList[glyphIdx];
else
@ -1069,7 +1054,7 @@ int Document::xposToGlyphStartChar(int line, const QString newStr, int xpos)
glyphPositionList = mLines[line]->glyphStartPositionList();
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<int> Document::calcGlyphPositionList(const QString &lineText, const QList<int> &glyphStartCharList, int left, int &right) const
{
return calcGlyphPositionList(lineText, glyphStartCharList,
mFontMetrics,
left,right);
}
QList<int> Document::calcGlyphPositionList(const QString &lineText, int &width) const
QList<int> GlyphCalculator::calcGlyphPositionList(const QString &lineText, int &width) const
{
QList<int> 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<int> &segList, int minVal, int maxVal, int v
return -1;
}
int Document::updateGlyphStartPositionList(
int GlyphCalculator::updateGlyphStartPositionList(
const QString &lineText,
const QList<int> &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<int> &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;i<mLines.size();i++) {
int width = mLines[i]->mWidth;
if ( width < 0 )
width = mGlyphCalculator.stringWidth(mLines[i]->lineText(),0);
if (width > maxWidth) {
maxWidth = width;
maxWidthLine = i;
}
}
if (maxWidthLine >= 0)
emit maxWidthLineFound(maxWidthLine);
}
}

View File

@ -36,6 +36,7 @@ QList<int> calcGlyphStartCharList(const QString &text);
void expandGlyphStartCharList(const QString& strAdded, int oldStrLen, QList<int> &glyphStartCharList);
class Document;
class FindMaxLineWidthThread;
using SearchConfirmAroundProc = std::function<bool ()>;
/**
@ -178,8 +179,9 @@ private:
int mWidth;
bool mIsTempWidth;
UpdateWidthFunc mUpdateWidthFunc;
qint64 mTimestamp;
friend class Document;
friend class FindMaxLineWidthThread;
};
typedef std::shared_ptr<DocumentLine> 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<int> calcGlyphPositionList(const QString& lineText, const QList<int> &glyphStartCharList,
const QFontMetrics &fontMetrics,
int left, int &right) const;
QList<int> calcGlyphPositionList(const QString& lineText, const QList<int> &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<int> calcLineWidth(const QString& lineText, const QList<int> &glyphStartCharList, int &width) {
return calcGlyphPositionList(lineText,glyphStartCharList,0,width);
}
QList<int> calcGlyphPositionList(const QString& lineText, int &width) const;
int updateGlyphStartPositionList(
const QString& lineText,
const QList<int> &glyphStartCharList,
int startChar, int endChar,
const QFontMetrics &fontMetrics,
QList<int> &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<int> &glyphStartCharList,
int startChar, int endChar,
const QFontMetrics &fontMetrics,
QList<int> &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<int> glyphPositionList, int xpos) const;
int charToGlyphIndex(const QString& str, QList<int> glyphStartCharList, int charPos) const;
QList<int> calcLineWidth(const QString& lineText, const QList<int> &glyphStartCharList, int &width);
QList<int> calcGlyphPositionList(const QString& lineText, const QList<int> &glyphStartCharList,
const QFontMetrics &fontMetrics,
int left, int &right) const;
QList<int> calcGlyphPositionList(const QString& lineText, const QList<int> &glyphStartCharList, int left, int &right) const;
QList<int> calcGlyphPositionList(const QString& lineText, int &width) const;
QList<int> getGlyphStartCharList(int line, const QString &lineText);
QList<int> getGlyphStartCharList(int line);
QList<int> 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;
};

View File

@ -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,

View File

@ -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<UpdateLineWidthEvent *>(event);
doUpdateLineWidth(updateEvent->line());
}
break;
case QEvent::KeyPress:{
QKeyEvent* keyEvent = static_cast<QKeyEvent *>(event);
QKeyEvent* keyEvent = dynamic_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab)
{
// process tab key presse event

View File

@ -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();