From cd75fefcc6189cdef5eb6350bb2e092916cdf015 Mon Sep 17 00:00:00 2001 From: "royqh1979@gmail.com" Date: Sun, 16 May 2021 20:36:00 +0800 Subject: [PATCH] work save --- RedPandaIDE/qsynedit/MiscProcs.cpp | 9 + RedPandaIDE/qsynedit/MiscProcs.h | 2 + RedPandaIDE/qsynedit/SynEdit.cpp | 14 +- RedPandaIDE/qsynedit/SynEdit.h | 16 +- RedPandaIDE/qsynedit/TextPainter.cpp | 588 ++++++++++++++++++++++++++- RedPandaIDE/qsynedit/TextPainter.h | 23 +- RedPandaIDE/qsynedit/Types.h | 19 + 7 files changed, 642 insertions(+), 29 deletions(-) diff --git a/RedPandaIDE/qsynedit/MiscProcs.cpp b/RedPandaIDE/qsynedit/MiscProcs.cpp index c7ff94d0..e1b264b3 100644 --- a/RedPandaIDE/qsynedit/MiscProcs.cpp +++ b/RedPandaIDE/qsynedit/MiscProcs.cpp @@ -531,3 +531,12 @@ int MulDiv(int a, int b, int c) //todo: handle overflow? return a*b/c; } + +SynFontStyles getFontStyles(const QFont &font) +{ + SynFontStyles styles; + styles.setFlag(SynFontStyle::fsBold, font.bold()); + styles.setFlag(SynFontStyle::fsItalic, font.italic()); + styles.setFlag(SynFontStyle::fsUnderline, font.underline()); + styles.setFlag(SynFontStyle::fsStrikeOut, font.strikeOut()); +} diff --git a/RedPandaIDE/qsynedit/MiscProcs.h b/RedPandaIDE/qsynedit/MiscProcs.h index 095fae87..6e33cb5b 100644 --- a/RedPandaIDE/qsynedit/MiscProcs.h +++ b/RedPandaIDE/qsynedit/MiscProcs.h @@ -87,4 +87,6 @@ uint16_t CalcFCS(unsigned char* ABuf, int ABufSize); void SynDrawGradient(QPaintDevice* ACanvas, const QColor& AStartColor, const QColor& AEndColor, int ASteps, const QRect& ARect, bool AHorizontal); +SynFontStyles getFontStyles(const QFont& font); + #endif // MISCPROCS_H diff --git a/RedPandaIDE/qsynedit/SynEdit.cpp b/RedPandaIDE/qsynedit/SynEdit.cpp index a2c45c23..c4356e54 100644 --- a/RedPandaIDE/qsynedit/SynEdit.cpp +++ b/RedPandaIDE/qsynedit/SynEdit.cpp @@ -334,7 +334,7 @@ DisplayCoord SynEdit::bufferToDisplayPos(const BufferCoord &p) if (i<=l && s[i] == '\t') x+=mTabWidth - (x % mTabWidth); else - x++; + x+=charColumns(s[i]); } result.Column = x + 1; } @@ -366,7 +366,7 @@ BufferCoord SynEdit::displayToBufferPos(const DisplayCoord &p) if (i < l && s[i] == '\t') x += mTabWidth - (x % mTabWidth); else - x += 1; + x += charColumns(s[i]); i++; } Result.Char = i; @@ -550,6 +550,16 @@ void SynEdit::clearUndo() mRedoList->Clear(); } +int SynEdit::charColumns(QChar ch) +{ + return std::ceil(fontMetrics().horizontalAdvance(ch) * dpiFactor() / mCharWidth); +} + +double SynEdit::dpiFactor() +{ + return fontMetrics().fontDpi() / 96.0; +} + void SynEdit::clearAreaList(SynEditingAreaList areaList) { areaList.clear(); diff --git a/RedPandaIDE/qsynedit/SynEdit.h b/RedPandaIDE/qsynedit/SynEdit.h index b6be93f6..15ecfec3 100644 --- a/RedPandaIDE/qsynedit/SynEdit.h +++ b/RedPandaIDE/qsynedit/SynEdit.h @@ -102,21 +102,8 @@ enum class SynReplaceAction { raCancel, raSkip, raReplace, raReplaceAll }; -struct SynEditingArea { - int beginX; - int endX; - QColor color; -}; -using PSynEditingArea = std::shared_ptr; -using SynEditingAreaList = QList; -enum class SynEditingAreaType { - eatRectangleBorder, - eatWaveUnderLine, - eatUnderLine -}; - enum class SynTransientType { ttBefore, ttAfter }; @@ -192,6 +179,8 @@ public: const BufferCoord& ptBefore, const BufferCoord& ptAfter); void clearUndo(); + int charColumns(QChar ch); + double dpiFactor(); int topLine() const; void setTopLine(int value); @@ -320,7 +309,6 @@ private: int mCharsInWindow; int mCharWidth; QFont mFontDummy; - QColor mColor; SynFontSmoothMethod mFontSmoothing; bool mMouseMoved; /* IME input */ diff --git a/RedPandaIDE/qsynedit/TextPainter.cpp b/RedPandaIDE/qsynedit/TextPainter.cpp index a1dff7f2..c6111294 100644 --- a/RedPandaIDE/qsynedit/TextPainter.cpp +++ b/RedPandaIDE/qsynedit/TextPainter.cpp @@ -13,7 +13,6 @@ QColor SynEditTextPainter::colEditorBG() if (edit->mActiveLineColor.isValid() && bCurrentLine) { return edit->mActiveLineColor; } else { - QColor result = edit->mColor; if (edit->mHighlighter) { PSynHighlighterAttribute attr = edit->mHighlighter->whitespaceAttribute(); if (attr && attr->background().isValid()) { @@ -21,7 +20,7 @@ QColor SynEditTextPainter::colEditorBG() } } } - return edit->mColor; + return edit->palette().color(QPalette::Base); } void SynEditTextPainter::ComputeSelectionInfo() @@ -88,16 +87,16 @@ int SynEditTextPainter::ColumnToXValue(int Col) return edit->mTextOffset + (Col - 1) * edit->mCharWidth; } -void SynEditTextPainter::PaintToken(const QString &Token, int TokenLen, int CharsBefore, int First, int Last, bool isSelection) +void SynEditTextPainter::PaintToken(const QString &Token, int TokenCols, int ColumnsBefore, int First, int Last, bool) { bool startPaint; int nX; if (Last >= First && rcToken.right() > rcToken.left()) { nX = ColumnToXValue(First); - First -= CharsBefore; - Last -= CharsBefore; - if (First > TokenLen) { + First -= ColumnsBefore; + Last -= ColumnsBefore; + if (First > TokenCols) { } else { painter->setClipRect(rcToken); int tokenColLen=0; @@ -105,9 +104,9 @@ void SynEditTextPainter::PaintToken(const QString &Token, int TokenLen, int Char for (int i=0;imTabWidth - ((CharsBefore+tokenColLen) % edit->mTabWidth); + charCols = edit->mTabWidth - ((ColumnsBefore+tokenColLen) % edit->mTabWidth); } else { - charCols = std::ceil(edit->fontMetrics().horizontalAdvance(Token[i]) * edit->fontMetrics().fontDpi() / 96.0 / edit->mCharWidth); + charCols = edit->charColumns(Token[i]); } if (tokenColLen+charCols>=First) { startPaint = true; @@ -128,3 +127,576 @@ void SynEditTextPainter::PaintToken(const QString &Token, int TokenLen, int Char } } +void SynEditTextPainter::PaintEditAreas(PSynEditingAreaList areaList) +{ + QRect rc; + int x1,x2; + int offset; + painter->setClipRect(rcLine); + rc=rcLine; + rc.setBottom(rc.bottom()-1); + setDrawingColors(false); + for (PSynEditingArea p:*areaList) { + if (p->beginX > LastCol) + continue; + if (p->endX < FirstCol) + continue; + if (p->beginX < FirstCol) + x1 = FirstCol; + else + x1 = p->beginX; + if (p->endX > LastCol) + x2 = LastCol; + else + x2 = p->endX; + rc.setLeft(ColumnToXValue(x1)); + rc.setRight(ColumnToXValue(x2)); + painter->setPen(p->color); + painter->setBrush(Qt::NoBrush); + switch(p->type) { + case SynEditingAreaType::eatRectangleBorder: + painter->drawRect(rc); + break; + case SynEditingAreaType::eatUnderLine: + painter->drawLine(rc.left(),rc.bottom(),rc.right(),rc.bottom()); + break; + case SynEditingAreaType::eatWaveUnderLine: + offset=3; + int lastX=rc.left(); + int lastY=rc.bottom()-offset; + int t = rc.left(); + while (t<=rc.right()) { + t+=3; + if (t>rc.right()) + t = rc.right(); + offset = 3 - offset; + painter->drawLine(lastX,lastY,t,rc.bottom()-offset); + lastX = t; + lastY = rc.bottom()-offset; + } + + } + } +} + +void SynEditTextPainter::PaintHighlightToken(bool bFillToEOL) +{ + bool bComplexToken; + int nC1, nC2, nC1Sel, nC2Sel; + bool bU1, bSel, bU2; + int nX1, nX2; + // Compute some helper variables. + nC1 = std::max(FirstCol, TokenAccu.ColumnsBefore + 1); + nC2 = std::min(LastCol, TokenAccu.ColumnsBefore + TokenAccu.Columns + 1); + if (bComplexLine) { + bU1 = (nC1 < nLineSelStart); + bSel = (nC1 < nLineSelEnd) && (nC2 >= nLineSelStart); + bU2 = (nC2 >= nLineSelEnd); + bComplexToken = bSel && (bU1 or bU2); + } else { + bSel = bLineSelected; + bComplexToken = false; +// bU1 = false; // to shut up compiler warning. +// bU2 = false; // to shut up compiler warning. + } + // Any token chars accumulated? + if (TokenAccu.Columns > 0) { + // Initialize the colors and the font style. + if (!bSpecialLine) { + colBG = TokenAccu.BG; + colFG = TokenAccu.FG; + } + + if (bSpecialLine && edit->mOptions.testFlag(eoSpecialLineDefaultFg)) + colFG = TokenAccu.FG; + QFont font = edit->font(); + font.setBold(TokenAccu.Style | SynFontStyle::fsBold); + font.setItalic(TokenAccu.Style | SynFontStyle::fsItalic); + font.setStrikeOut(TokenAccu.Style | SynFontStyle::fsStrikeOut); + font.setUnderline(TokenAccu.Style | SynFontStyle::fsUnderline); + painter->setFont(font); + // Paint the chars + if (bComplexToken) { + // first unselected part of the token + if (bU1) { + setDrawingColors(false); + rcToken.setRight(ColumnToXValue(nLineSelStart)); + PaintToken(TokenAccu.s,TokenAccu.Columns,TokenAccu.ColumnsBefore,nC1,nLineSelStart,false); + } + // selected part of the token + setDrawingColors(true); + nC1Sel = std::max(nLineSelStart, nC1); + nC2Sel = std::min(nLineSelEnd, nC2); + rcToken.setRight(ColumnToXValue(nC2Sel)); + PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore, nC1Sel, nC2Sel,true); + // second unselected part of the token + if (bU2) { + setDrawingColors(false); + rcToken.setRight(ColumnToXValue(nC2)); + PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore,nLineSelEnd, nC2,false); + } + } else { + setDrawingColors(bSel); + rcToken.setRight(ColumnToXValue(nC2)); + PaintToken(TokenAccu.s, TokenAccu.Columns, TokenAccu.ColumnsBefore, nC1, nC2,bSel); + } + } + + // Fill the background to the end of this line if necessary. + if (bFillToEOL && rcToken.left() < rcLine.right()) { + if (!bSpecialLine) + colBG = colEditorBG(); + if (bComplexLine) { + nX1 = ColumnToXValue(nLineSelStart); + nX2 = ColumnToXValue(nLineSelEnd); + if (rcToken.left() < nX1) { + setDrawingColors(false); + rcToken.setRight(nX1); +// if (TokenAccu.Len != 0 && TokenAccu.Style != SynFontStyle::fsNone) +// AdjustEndRect(); + painter->setPen(Qt::NoPen); + painter->drawRect(rcToken); + rcToken.setLeft(nX1); + } + if (rcToken.left() < nX2) { + setDrawingColors(true); + rcToken.setRight(nX2); + painter->setPen(Qt::NoPen); + painter->drawRect(rcToken); + rcToken.setLeft(nX2); + } + if (rcToken.left() < rcLine.right()) { + setDrawingColors(false); + rcToken.setRight(rcLine.right()); + painter->setPen(Qt::NoPen); + painter->drawRect(rcToken); + } + } else { + setDrawingColors(bLineSelected); + rcToken.setRight(rcLine.right()); +// if (TokenAccu.Len != 0 && TokenAccu.Style != SynFontStyle::fsNone) +// AdjustEndRect(); + painter->setPen(Qt::NoPen); + painter->drawRect(rcToken); + } + } +} + +bool SynEditTextPainter::TokenIsSpaces(bool &bSpacesTest, const QString& Token, bool& bIsSpaces) +{ + QString pTok; + if (!bSpacesTest) { + bSpacesTest = true; + for (QChar ch:Token) { + //todo: should include tabs? + if (ch!= ' ') { + bIsSpaces = false; + return bIsSpaces; + } + } + bIsSpaces = true; + } + return bIsSpaces; +} + +// Store the token chars with the attributes in the TokenAccu +// record. This will paint any chars already stored if there is +// a (visible) change in the attributes. +void SynEditTextPainter::AddHighlightToken(const QString &Token, int ColumnsBefore, int TokenColumns, int cLine, PSynHighlighterAttribute p_Attri) +{ + bool bCanAppend; + QColor Foreground, Background; + SynFontStyles Style; + bool bSpacesTest,bIsSpaces; + + if (p_Attri) { + Foreground = p_Attri->foreground(); + Background = p_Attri->background(); + Style = p_Attri->styles(); + } else { + Foreground = colFG; + Background = colBG; + Style = getFontStyles(edit->font()); + } + + if (Background.isValid() || (edit->mActiveLineColor.isValid() && bCurrentLine)) { + Background = colEditorBG(); + } + if (Background.isValid()) { + Foreground = edit->palette().color(QPalette::Text); + } + + //todo : change char(getTokenPos) to column? + if (edit->mOnPaintHighlightToken) + edit->mOnPaintHighlightToken(cLine,edit->mHighlighter->getTokenPos(), + Token,p_Attri,Style,Foreground,Background); + + // Do we have to paint the old chars first, or can we just append? + bCanAppend = false; + bSpacesTest = false; + if (TokenAccu.Columns > 0) { + // font style must be the same or token is only spaces + if (TokenAccu.Style == Style || ( (Style & SynFontStyle::fsUnderline) == (TokenAccu.Style & fsUnderline) + && TokenIsSpaces(bSpacesTest,Token,bIsSpaces)) ) { + // either special colors or same colors + if ((bSpecialLine && !(edit->mOptions.testFlag(SynEditorOption::eoSpecialLineDefaultFg))) || bLineSelected || + // background color must be the same and + ((TokenAccu.BG == Background) && + // foreground color must be the same or token is only spaces + ((TokenAccu.FG == Foreground) || (TokenIsSpaces(bSpacesTest,Token,bIsSpaces) && !edit->mShowSpecChar)))) { + bCanAppend = true; + } + } + // If we can't append it, then we have to paint the old token chars first. + if (!bCanAppend) + PaintHighlightToken(false); + } + // Don't use AppendStr because it's more expensive. + if (bCanAppend) { + TokenAccu.s.append(Token); + TokenAccu.Columns+=TokenColumns; + } else { + TokenAccu.Columns = TokenColumns; + TokenAccu.s = Token; + TokenAccu.ColumnsBefore = ColumnsBefore; + TokenAccu.FG = Foreground; + TokenAccu.BG = Background; + TokenAccu.Style = Style; + } +} + +void SynEditTextPainter::PaintFoldAttributes() +{ + int i, TabSteps, LineIndent, LastNonBlank, X, Y, cRow, vLine; + QBrush DottedPenDesc; + // Paint indent guides. Use folds to determine indent value of these + // Use a separate loop so we can use a custom pen + // Paint indent guides using custom pen + if (edit->mCodeFolding.indentGuides) { + QPen dottedPen(Qt::PenStyle::DashLine); + dottedPen.setColor(edit->mCodeFolding.indentGuidesColor); + + QPen oldPen = painter->pen(); + painter->setPen(dottedPen); + // Now loop through all the lines. The indices are valid for Lines. + for (cRow = aFirstRow; cRow<=aLastRow;cRow++) { + vLine = edit->rowToLine(cRow); + if (vLine > edit->mLines->count() && edit->mLines->count() > 0) + break; + // Set vertical coord + Y = (vLine - edit->mTopLine) * edit->mTextHeight; // limit inside clip rect + if (edit->mTextHeight % 2 == 1 && vLine % 2 == 0) { + Y++; + } + // Get next nonblank line + LastNonBlank = vLine - 1; + while (LastNonBlank + 1 < edit->mLines->count() && edit->mLines->getString(LastNonBlank).trimmed().isEmpty()) + LastNonBlank++; + LineIndent = edit->GetLineIndent(edit->mLines->getString(LastNonBlank)); + // Step horizontal coord + TabSteps = edit->mTabWidth; + while (TabSteps < LineIndent) { + X = TabSteps * edit->mCharWidth + edit->mTextOffset - 2; + TabSteps+=edit->mTabWidth; + + // Move to top of vertical line + painter->drawLine(X,Y,X,Y+edit->mTextHeight); + } + } + painter->setPen(oldPen); + } + + if (!edit->mUseCodeFolding) + exit; + + // Paint collapsed lines using changed pen + if (edit->mCodeFolding.showCollapsedLine) { + painter->setPen(edit->mCodeFolding.collapsedLineColor); + for (int i=0; i< edit->mAllFoldRanges.count();i++) { + PSynEditFoldRange range = edit->mAllFoldRanges[i]; + if (range->collapsed && !range->parentCollapsed() && + (range->fromLine <= vLastLine) && (range->fromLine >= vFirstLine) ) { + // Get starting and end points + Y = (edit->lineToRow(range->fromLine) - edit->mTopLine + 1) * edit->mTextHeight - 1; + painter->drawLine(AClip.left(),Y, AClip.right(),Y); + } + } + } + +} + +void SynEditTextPainter::GetBraceColorAttr(int level, PSynHighlighterAttribute &attr) +{ + if (!edit->mOptions.testFlag(SynEditorOption::eoShowRainbowColor)) + return; + if (attr != edit->mHighlighter->symbolAttribute()) + return; + switch(level % 4) { + case 0: + attr = edit->mHighlighter->keywordAttribute(); + case 1: + attr = edit->mHighlighter->symbolAttribute(); + case 2: + attr = edit->mHighlighter->stringAttribute(); + case 3: + attr = edit->mHighlighter->identifierAttribute(); + } +} + +void SynEditTextPainter::PaintLines() +{ + int cRow; // row index for the loop + int vLine; + QString sLine; // the current line + QString sToken; // highlighter token info + int nTokenPos, nTokenLen; + PSynHighlighterAttribute attr; + int vFirstChar; + int vLastChar; + PSynEditingAreaList areaList; + QColor colBorder; + PSynEditFoldRange foldRange; + int nC1,nC2,nFold; + QString sFold; + + // Initialize rcLine for drawing. Note that Top and Bottom are updated + // inside the loop. Get only the starting point for this. + rcLine = AClip; + rcLine.setBottom((aFirstRow - edit->mTopLine) * edit->mTextHeight); + // Make sure the token accumulator string doesn't get reassigned too often. + TokenAccu.Columns = 0; + TokenAccu.ColumnsBefore = 0; + if (fHighlighter) then begin + TokenAccu.MaxLen := Max(128, fCharsInWindow); + SetLength(TokenAccu.s, TokenAccu.MaxLen); + end; + // Now loop through all the lines. The indices are valid for Lines. + for cRow := aFirstRow to aLastRow do begin + vLine := RowToLine(cRow); + if (vLine > Lines.Count) and not (Lines.Count = 0) then + break; + + // Get the expanded line. + sLine := Lines.ExpandedStrings[vLine - 1]; + // determine whether will be painted with ActiveLineColor + bCurrentLine := CaretY = vLine; + // Initialize the text and background colors, maybe the line should + // use special values for them. + colFG := Font.Color; + colBG := colEditorBG; + bSpecialLine := DoOnSpecialLineColors(vLine, colFG, colBG); + if bSpecialLine then begin + // The selection colors are just swapped, like seen in Delphi. + colSelFG := colBG; + colSelBG := colFG; + end else begin + colSelFG := fSelectedColor.Foreground; + colSelBG := fSelectedColor.Background; + DoOnEditAreas(vLine, areaList,colBorder,areaType); + end; + + // Removed word wrap support + vFirstChar := FirstCol; + vLastChar := LastCol; + + // Get the information about the line selection. Three different parts + // are possible (unselected before, selected, unselected after), only + // unselected or only selected means bComplexLine will be FALSE. Start + // with no selection, compute based on the visible columns. + bComplexLine := FALSE; + nLineSelStart := 0; + nLineSelEnd := 0; + // Does the selection intersect the visible area? + if bAnySelection and (cRow >= vSelStart.Row) and (cRow <= vSelEnd.Row) then begin + // Default to a fully selected line. This is correct for the smLine + // selection mode and a good start for the smNormal mode. + nLineSelStart := FirstCol; + nLineSelEnd := LastCol + 1; + if (fActiveSelectionMode = smColumn) or + ((fActiveSelectionMode = smNormal) and (cRow = vSelStart.Row)) then + if (vSelStart.Column > LastCol) then begin + nLineSelStart := 0; + nLineSelEnd := 0; + end else if (vSelStart.Column > FirstCol) then begin + nLineSelStart := vSelStart.Column; + bComplexLine := TRUE; + end; + if (fActiveSelectionMode = smColumn) or + ((fActiveSelectionMode = smNormal) and (cRow = vSelEnd.Row)) then + if (vSelEnd.Column < FirstCol) then begin + nLineSelStart := 0; + nLineSelEnd := 0; + end else if (vSelEnd.Column < LastCol) then begin + nLineSelEnd := vSelEnd.Column; + bComplexLine := TRUE; + end; + {$IFDEF SYN_MBCSSUPPORT} + //todo: nLineSelStart & nLineSelEnd must be buffer coordinates + if (fActiveSelectionMode = smColumn) then + MBCSGetSelRangeInLineWhenColumnSelectionMode(sLine, nLineSelStart, + nLineSelEnd); + {$ENDIF} + end; //endif bAnySelection + + // Update the rcLine rect to this line. + rcLine.Top := rcLine.Bottom; + Inc(rcLine.Bottom, fTextHeight); + + bLineSelected := (not bComplexLine) and (nLineSelStart > 0); + rcToken := rcLine; + + if not Assigned(fHighlighter) or (not fHighlighter.Enabled) then begin + // Remove text already displayed (in previous rows) + if (vFirstChar <> FirstCol) or (vLastChar <> LastCol) then + sToken := Copy(sLine, vFirstChar, vLastChar - vFirstChar) + else + sToken := sLine; + if fShowSpecChar and (not bLineSelected) and (not bSpecialLine) and (Length(sLine) < vLastChar) then + sToken := sToken + SynLineBreakGlyph; + nTokenLen := Length(sToken); + if bComplexLine then begin + SetDrawingColors(FALSE); + rcToken.Left := Max(rcLine.Left, ColumnToXValue(FirstCol)); + rcToken.Right := Min(rcLine.Right, ColumnToXValue(nLineSelStart)); + PaintToken(sToken, nTokenLen, 0, FirstCol, nLineSelStart,False); + rcToken.Left := Max(rcLine.Left, ColumnToXValue(nLineSelEnd)); + rcToken.Right := Min(rcLine.Right, ColumnToXValue(LastCol)); + PaintToken(sToken, nTokenLen, 0, nLineSelEnd, LastCol,True); + SetDrawingColors(TRUE); + rcToken.Left := Max(rcLine.Left, ColumnToXValue(nLineSelStart)); + rcToken.Right := Min(rcLine.Right, ColumnToXValue(nLineSelEnd)); + PaintToken(sToken, nTokenLen, 0, nLineSelStart, nLineSelEnd - 1,False); + end else begin + SetDrawingColors(bLineSelected); + PaintToken(sToken, nTokenLen, 0, FirstCol, LastCol,bLineSelected); + end; + end else begin + // Initialize highlighter with line text and range info. It is + // necessary because we probably did not scan to the end of the last + // line - the internal highlighter range might be wrong. + if vLine = 1 then begin + fHighlighter.ResetRange; + fHighlighter.ResetParenthesisLevel; + fHighlighter.ResetBracketLevel; + fHighlighter.ResetBraceLevel; + end else begin + fHighlighter.SetRange(Lines.Ranges[vLine - 2]); + fHighlighter.SetParenthesisLevel(Lines.ParenthesisLevels[vLine - 2]); + fHighlighter.SetBracketLevel(Lines.BracketLevels[vLine - 2]); + fHighlighter.SetBraceLevel(Lines.BraceLevels[vLine - 2]); + end; + fHighlighter.SetLine(sLine, vLine - 1); + // Try to concatenate as many tokens as possible to minimize the count + // of ExtTextOut calls necessary. This depends on the selection state + // or the line having special colors. For spaces the foreground color + // is ignored as well. + TokenAccu.Len := 0; + nTokenPos := 0; + // Test first whether anything of this token is visible. + while not fHighlighter.GetEol do begin + sToken := fHighlighter.GetToken; + // Work-around buggy highlighters which return empty tokens. + if sToken = '' then begin + fHighlighter.Next; + if fHighlighter.GetEol then + break; + sToken := fHighlighter.GetToken; + // Maybe should also test whether GetTokenPos changed... + if sToken = '' then + raise Exception.Create('The highlighter seems to be in an infinite loop'); + end; + nTokenPos := fHighlighter.GetTokenPos; + nTokenLen := Length(sToken); + if nTokenPos + nTokenLen >= vFirstChar then begin + if nTokenPos + nTokenLen >= vLastChar then begin + if nTokenPos >= vLastChar then + break; //*** BREAK *** + + nTokenLen := vLastChar - nTokenPos - 1; + end; + // It's at least partially visible. Get the token attributes now. + attr := fHighlighter.GetTokenAttribute; + { + if (nTokenPos = 0) and (attr = fHighlighter.WhitespaceAttribute) then begin + sToken := StringOfChar('.',nTokenLen); + end; + } + if sToken = '[' then begin + GetBraceColorAttr(fHighlighter.GetBracketLevel,attr); + end else if sToken = ']' then begin + GetBraceColorAttr(fHighlighter.GetBracketLevel+1,attr); + end else if sToken = '(' then begin + GetBraceColorAttr(fHighlighter.GetParenthesisLevel,attr); + end else if sToken = ')' then begin + GetBraceColorAttr(fHighlighter.GetParenthesisLevel+1,attr); + end else if sToken = '{' then begin + GetBraceColorAttr(fHighlighter.GetBraceLevel,attr); + end else if sToken = '}' then begin + GetBraceColorAttr(fHighlighter.GetBraceLevel+1,attr); + end; + AddHighlightToken(sToken, nTokenPos - (vFirstChar - FirstCol), + nTokenLen, cRow,attr); + end; + // Let the highlighter scan the next token. + fHighlighter.Next; + end; + // Don't assume HL.GetTokenPos is valid after HL.GetEOL = True. + Inc(nTokenPos, Length(sToken)); + if fHighlighter.GetEol and (nTokenPos < vLastChar) then begin + // Draw text that couldn't be parsed by the highlighter, if any. + if nTokenPos < Length(sLine) then begin + if nTokenPos + 1 < vFirstChar then + nTokenPos := vFirstChar - 1; + nTokenLen := Min(Length(sLine), vLastChar) - (nTokenPos + 1); + if nTokenLen > 0 then begin + sToken := Copy(sLine, nTokenPos + 1, nTokenLen); + AddHighlightToken(sToken, nTokenPos - (vFirstChar - FirstCol), + nTokenLen, cRow, nil); + end; + end; + // Draw LineBreak glyph. + if (eoShowSpecialChars in fOptions) and (not bLineSelected) + and (not bSpecialLine) and (Length(sLine) < vLastChar) then begin + AddHighlightToken(SynLineBreakGlyph, + Length(sLine) - (vFirstChar - FirstCol), + Length(SynLineBreakGlyph),cRow, fHighLighter.WhitespaceAttribute); + end; + end; + + // Paint folding + foldRange := FoldStartAtLine(vLine); + if assigned(foldRange) and foldRange.Collapsed then begin + sFold := ' ... }'; + nFold := Length(sFold); + Attr := fHighlighter.SymbolAttribute; + GetBraceColorAttr(fHighlighter.GetBraceLevel,attr); + AddHighlightToken(sFold,Length(sLine)+1 - (vFirstChar - FirstCol) + , nFold, cRow, attr); + // Compute some helper variables. + //nC1 := Max(FirstCol, Length(sLine)+1); + //nC2 := Min(LastCol, Length(sLine) +1 + nFold + 1); + //SetDrawingColors(FALSE); + //PaintToken(sFold,nFold, Length(sLine)+1,nC1, nC2); + end; + + // Draw anything that's left in the TokenAccu record. Fill to the end + // of the invalid area with the correct colors. + PaintHighlightToken(TRUE); + + //Paint editingAreaBorders + PaintEditAreas(areaList,colBorder,areaType); + + end; + + // Now paint the right edge if necessary. We do it line by line to reduce + // the flicker. Should not cost very much anyway, compared to the many + // calls to ExtTextOut. + if bDoRightEdge then begin + Canvas.MoveTo(nRightEdge, rcLine.Top); + Canvas.LineTo(nRightEdge, rcLine.Bottom + 1); + end; + bCurrentLine := False; + end; //endfor cRow +} + + diff --git a/RedPandaIDE/qsynedit/TextPainter.h b/RedPandaIDE/qsynedit/TextPainter.h index 17ea7d12..82951bc6 100644 --- a/RedPandaIDE/qsynedit/TextPainter.h +++ b/RedPandaIDE/qsynedit/TextPainter.h @@ -5,14 +5,14 @@ #include #include #include "Types.h" +#include "highlighter/base.h" class SynEdit; class SynEditTextPainter { - struct TokenAccu { - int Len; - int MaxLen; - int CharsBefore; + struct SynTokenAccu { + int Columns; + int ColumnsBefore; QString s; QColor FG; QColor BG; @@ -26,8 +26,17 @@ public: void ComputeSelectionInfo(); void setDrawingColors(bool Selected); int ColumnToXValue(int Col); - void PaintToken(const QString& Token, int TokenLen, int CharsBefore, + void PaintToken(const QString& Token, int TokenLen, int ColumnsBefore, int First, int Last, bool isSelection); + void PaintEditAreas(PSynEditingAreaList areaList); + void PaintHighlightToken(bool bFillToEOL); + bool TokenIsSpaces(bool& bSpacesTest, const QString& Token, bool& bIsSpaces); + void AddHighlightToken(const QString& Token, int ColumnsBefore, int TokenColumns, + int cLine, PSynHighlighterAttribute p_Attri); + + void PaintFoldAttributes(); + void GetBraceColorAttr(int level, const PSynHighlighterAttribute& attr); + void PaintLines(); private: SynEdit* edit; QPainter* painter; @@ -47,6 +56,10 @@ private: // painting the background and the text QRect rcLine, rcToken; int vFirstLine, vLastLine; + + QRect AClip; + int aFirstRow, aLastRow, FirstCol, LastCol; + SynTokenAccu TokenAccu; }; #endif // TEXTPAINTER_H diff --git a/RedPandaIDE/qsynedit/Types.h b/RedPandaIDE/qsynedit/Types.h index ee49bad7..87aa8c02 100644 --- a/RedPandaIDE/qsynedit/Types.h +++ b/RedPandaIDE/qsynedit/Types.h @@ -33,4 +33,23 @@ using PSynIcon = std::shared_ptr; using SynIconList = QList; using PSynIconList = std::shared_ptr; +enum class SynEditingAreaType { + eatRectangleBorder, + eatWaveUnderLine, + eatUnderLine +}; + +struct SynEditingArea { + int beginX; + int endX; + QColor color; + SynEditingAreaType type; +}; + + +using PSynEditingArea = std::shared_ptr; +using SynEditingAreaList = QList; +using PSynEditingAreaList = std::shared_ptr; + + #endif // TYPES_H