From 0fc995e56a30f1174b51bc30b4d2c952c8e69793 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Fri, 25 Mar 2022 22:13:00 +0800 Subject: [PATCH] - enhancement: draw cursor for column mode - enahcnement: edit/delete in multiline ( column mode), press ese to exit --- NEWS.md | 4 +- RedPandaIDE/editor.cpp | 2 +- RedPandaIDE/qsynedit/SynEdit.cpp | 204 ++++++++++++++++++++------- RedPandaIDE/qsynedit/TextPainter.cpp | 6 - 4 files changed, 155 insertions(+), 61 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9ab9bfd9..ea26e36f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,9 +18,9 @@ Red Panda C++ Version 1.0.1 - enhancement: switch capslock won't cancel code completion - enhancement: double click on item in code completion list will use it to complete - fix: goto declaration by ctrl+click will incorrectly select contents - - enhancement: alt+shift+arrow do column selection - fix: input may cause error, if selection in column mode and begin/end at the same column - - enhancement: draw selection line if selection in column mode and begin/end at the same column + - enhancement: draw cursor for column mode + - enahcnement: edit/delete in multiline ( column mode), press ese to exit Red Panda C++ Version 1.0.0 - fix: calculation for code snippets's tab stop positions is not correct diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index b1c337c7..97271a3d 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -1515,7 +1515,7 @@ void Editor::onStatusChanged(SynStatusChanges changes) clearUserCodeInTabStops(); } } - } else if (!selAvail() && highlighter() && pSettings->editor().highlightMathingBraces()){ + } else if (!selAvail() &&!colSelAvail() && highlighter() && pSettings->editor().highlightMathingBraces()){ invalidateLine(mHighlightCharPos1.Line); invalidateLine(mHighlightCharPos2.Line); mHighlightCharPos1 = BufferCoord{0,0}; diff --git a/RedPandaIDE/qsynedit/SynEdit.cpp b/RedPandaIDE/qsynedit/SynEdit.cpp index b1ed4024..8cafa37e 100644 --- a/RedPandaIDE/qsynedit/SynEdit.cpp +++ b/RedPandaIDE/qsynedit/SynEdit.cpp @@ -1039,7 +1039,8 @@ bool SynEdit::selAvail() const bool SynEdit::colSelAvail() const { - return (mActiveSelectionMode == SynSelectionMode::smColumn && mBlockBegin.Line!=mBlockEnd.Line); + return (mActiveSelectionMode == SynSelectionMode::smColumn + && (mBlockBegin.Line!=mBlockEnd.Line || mBlockBegin.Char!=mBlockEnd.Char)); } QString SynEdit::wordAtCursor() @@ -1891,6 +1892,20 @@ void SynEdit::doDeleteLastChar() doOnPaintTransientEx(SynTransientType::ttAfter, true); }); + if (mActiveSelectionMode==SynSelectionMode::smColumn) { + BufferCoord start=blockBegin(); + BufferCoord end=blockEnd(); + if (!selAvail()) { + start.Char--; + setBlockBegin(start); + setBlockEnd(end); + } + setSelectedTextEmpty(); + end.Char = start.Char; + setBlockBegin(start); + setBlockEnd(end); + return; + } if (selAvail()) { setSelectedTextEmpty(); return; @@ -2005,38 +2020,53 @@ void SynEdit::doDeleteCurrentChar() BufferCoord Caret; if (!mReadOnly) { doOnPaintTransient(SynTransientType::ttBefore); - if (selAvail()) - setSelectedTextEmpty(); - else { - // Call UpdateLastCaretX. Even though the caret doesn't move, the - // current caret position should "stick" whenever text is modified. - updateLastCaretX(); - QString Temp = lineText(); - int Len = Temp.length(); - if (mCaretX <= Len) { - // delete char - helper = Temp.mid(mCaretX-1, 1); - Caret.Char = mCaretX + 1; - Caret.Line = mCaretY; - Temp.remove(mCaretX-1, 1); - properSetLine(mCaretY - 1, Temp); - } else { - // join line with the line after - if (mCaretY < mLines->count()) { - properSetLine(mCaretY - 1, Temp + mLines->getString(mCaretY)); - Caret.Char = 1; - Caret.Line = mCaretY + 1; - helper = lineBreak(); - mLines->deleteAt(mCaretY); - if (mCaretX==1) - doLinesDeleted(mCaretY, 1); - else - doLinesDeleted(mCaretY + 1, 1); - } + + if (mActiveSelectionMode==SynSelectionMode::smColumn) { + BufferCoord start=blockBegin(); + BufferCoord end=blockEnd(); + if (!selAvail()) { + end.Char++; + setBlockBegin(start); + setBlockEnd(end); } - if ((Caret.Char != mCaretX) || (Caret.Line != mCaretY)) { - mUndoList->AddChange(SynChangeReason::crSilentDeleteAfterCursor, caretXY(), Caret, - helper, SynSelectionMode::smNormal); + setSelectedTextEmpty(); + end.Char = start.Char; + setBlockBegin(start); + setBlockEnd(end); + } else { + if (selAvail()) + setSelectedTextEmpty(); + else { + // Call UpdateLastCaretX. Even though the caret doesn't move, the + // current caret position should "stick" whenever text is modified. + updateLastCaretX(); + QString Temp = lineText(); + int Len = Temp.length(); + if (mCaretX <= Len) { + // delete char + helper = Temp.mid(mCaretX-1, 1); + Caret.Char = mCaretX + 1; + Caret.Line = mCaretY; + Temp.remove(mCaretX-1, 1); + properSetLine(mCaretY - 1, Temp); + } else { + // join line with the line after + if (mCaretY < mLines->count()) { + properSetLine(mCaretY - 1, Temp + mLines->getString(mCaretY)); + Caret.Char = 1; + Caret.Line = mCaretY + 1; + helper = lineBreak(); + mLines->deleteAt(mCaretY); + if (mCaretX==1) + doLinesDeleted(mCaretY, 1); + else + doLinesDeleted(mCaretY + 1, 1); + } + } + if ((Caret.Char != mCaretX) || (Caret.Line != mCaretY)) { + mUndoList->AddChange(SynChangeReason::crSilentDeleteAfterCursor, caretXY(), Caret, + helper, SynSelectionMode::smNormal); + } } } doOnPaintTransient(SynTransientType::ttAfter); @@ -2499,13 +2529,23 @@ QRect SynEdit::calculateCaretRect() const + lineText().mid(mCaretX-1); coord.Column = charToColumn(sLine,mCaretX+mInputPreeditString.length()); } + if (mActiveSelectionMode == SynSelectionMode::smColumn) { + coord.Row = lineToRow(blockBegin().Line); + } QPoint caretPos = rowColumnToPixels(coord); int caretWidth=mCharWidth; if (mCaretY <= mLines->count() && mCaretX <= mLines->getString(mCaretY-1).length()) { caretWidth = charColumns(mLines->getString(mCaretY-1)[mCaretX-1])*mCharWidth; } - return QRect(caretPos.x(),caretPos.y(),caretWidth, - mTextHeight); + if (mActiveSelectionMode == SynSelectionMode::smColumn) { + return QRect(caretPos.x(),caretPos.y(),caretWidth, + mTextHeight*(lineToRow(blockEnd().Line)- + lineToRow(blockBegin().Line)+1)); + + } else { + return QRect(caretPos.x(),caretPos.y(),caretWidth, + mTextHeight); + } } QRect SynEdit::calculateInputCaretRect() const @@ -2732,9 +2772,19 @@ void SynEdit::doAddChar(QChar AChar) //DoOnPaintTransient(ttBefore); //mCaretX will change after setSelLength; if ((mInserting == false) && (!selAvail())) { - setSelLength(1); + if (colSelAvail()) { + BufferCoord start=blockBegin(); + BufferCoord end=blockEnd(); + end.Char++; + setBlockBegin(start); + setBlockEnd(end); + } else + setSelLength(1); } + bool addInColumnMode = (mActiveSelectionMode == SynSelectionMode::smColumn); + BufferCoord oldBlockBegin = blockBegin(); + BufferCoord oldBlockEnd = blockEnd(); if (isIdentChar(AChar)) { doSetSelText(AChar); } else if (AChar.isSpace()) { @@ -2755,7 +2805,8 @@ void SynEdit::doAddChar(QChar AChar) int oldCaretX=mCaretX-1; int oldCaretY=mCaretY; // auto - if (mOptions.testFlag(eoAutoIndent) + if (mActiveSelectionMode==SynSelectionMode::smNormal + && mOptions.testFlag(eoAutoIndent) && mHighlighter && mHighlighter->getClass()==SynHighlighterClass::CppHighlighter && (oldCaretY<=mLines->count()) ) { @@ -2822,6 +2873,13 @@ void SynEdit::doAddChar(QChar AChar) } mUndoList->EndBlock(); } + if (addInColumnMode) { + oldBlockBegin.Char = mCaretX; + oldBlockEnd.Char = mCaretX; + setBlockBegin(oldBlockBegin); + setBlockEnd(oldBlockEnd); + setActiveSelectionMode(SynSelectionMode::smColumn); + } //DoOnPaintTransient(ttAfter); } @@ -3755,7 +3813,7 @@ void SynEdit::paintCaret(QPainter &painter, const QRect rcClip) switch(ct) { case SynEditCaretType::ctVerticalLine: { QRect caretRC; - int size = std::max(1,(rcClip.bottom()-rcClip.top())/15); + int size = std::max(1, mTextHeight/15); caretRC.setLeft(rcClip.left()+1); caretRC.setTop(rcClip.top()); caretRC.setBottom(rcClip.bottom()); @@ -3765,7 +3823,7 @@ void SynEdit::paintCaret(QPainter &painter, const QRect rcClip) } case SynEditCaretType::ctHorizontalLine: { QRect caretRC; - int size = std::max(1,(rcClip.bottom()-rcClip.top())/15); + int size = std::max(1,mTextHeight/15); caretRC.setLeft(rcClip.left()); caretRC.setTop(rcClip.bottom()-1+size); caretRC.setBottom(rcClip.bottom()-1); @@ -4096,12 +4154,29 @@ void SynEdit::setOptions(const SynEditorOptions &Value) void SynEdit::doAddStr(const QString &s) { if (!selAvail() && !mInserting) { - BufferCoord BB = caretXY(); - BufferCoord BE = BB; - BE.Char = BB.Char + s.length(); - setCaretAndSelection(caretXY(),BB,BE); + if (colSelAvail()) { + BufferCoord start=blockBegin(); + BufferCoord end=blockEnd(); + end.Char+=s.length(); + setCaretAndSelection(caretXY(),start,end); + } else { + BufferCoord BB = caretXY(); + BufferCoord BE = BB; + BE.Char = BB.Char + s.length(); + setCaretAndSelection(caretXY(),BB,BE); + } } + bool addInColumnMode = (mActiveSelectionMode == SynSelectionMode::smColumn); + BufferCoord oldBlockBegin = blockBegin(); + BufferCoord oldBlockEnd = blockEnd(); doSetSelText(s); + if (addInColumnMode) { + oldBlockBegin.Char = mCaretX; + oldBlockEnd.Char = mCaretX; + setBlockBegin(oldBlockBegin); + setBlockEnd(oldBlockEnd); + setActiveSelectionMode(SynSelectionMode::smColumn); + } } void SynEdit::doUndo() @@ -4897,6 +4972,11 @@ void SynEdit::setSelTextPrimitiveEx(SynSelectionMode PasteMode, const QString &V BufferCoord BE = blockEnd(); if (selAvail()) { deleteSelection(BB,BE); + if (mActiveSelectionMode == SynSelectionMode::smColumn) { + BE.Char = BB.Char; + mBlockBegin = BB; + mBlockEnd = BE; + } internalSetCaretXY(BB); } if (!Value.isEmpty()) { @@ -5362,8 +5442,11 @@ int SynEdit::insertTextByColumnMode(const QString &value, bool addToUndoList) int insertCol; BufferCoord lineBreakPos; int result = 0; - // Insert string at current position - insertCol = charToColumn(mCaretY,mCaretX); + BufferCoord insertPos = blockBegin(); + BufferCoord endPos = blockEnd(); + // Insert string at begin of the selection + insertCol = charToColumn(insertPos.Line,insertPos.Char); + mCaretY=insertPos.Line; start = 0; do { p = GetEOL(value,start); @@ -5406,7 +5489,17 @@ int SynEdit::insertTextByColumnMode(const QString &value, bool addToUndoList) mCaretY++; mStatusChanges.setFlag(SynStatusChange::scCaretY); } - start = p; + //if we only have one inline to insert, repeat it + if (start == 0 && p>=value.length() && p!=start) { + p=0; + if (mCaretY < endPos.Line) { + mCaretY++; + mStatusChanges.setFlag(SynStatusChange::scCaretY); + } else + break; + } else { + start = p; + } } while (pkey(),event->modifiers()); - if (cmd!=SynEditorCommand::ecNone) { - commandProcessor(cmd,QChar(),nullptr); + if (event->key() == Qt::Key_Escape && mActiveSelectionMode != mSelectionMode) { + setActiveSelectionMode(selectionMode()); + setBlockBegin(caretXY()); + setBlockEnd(caretXY()); event->accept(); - } else if (!event->text().isEmpty()) { - QChar c = event->text().at(0); - if (c=='\t' || c.isPrint()) { - commandProcessor(SynEditorCommand::ecChar,c,nullptr); + } else { + SynEditorCommand cmd=TranslateKeyCode(event->key(),event->modifiers()); + if (cmd!=SynEditorCommand::ecNone) { + commandProcessor(cmd,QChar(),nullptr); event->accept(); + } else if (!event->text().isEmpty()) { + QChar c = event->text().at(0); + if (c=='\t' || c.isPrint()) { + commandProcessor(SynEditorCommand::ecChar,c,nullptr); + event->accept(); + } } } if (!event->isAccepted()) { diff --git a/RedPandaIDE/qsynedit/TextPainter.cpp b/RedPandaIDE/qsynedit/TextPainter.cpp index 41e7397d..044eb5b0 100644 --- a/RedPandaIDE/qsynedit/TextPainter.cpp +++ b/RedPandaIDE/qsynedit/TextPainter.cpp @@ -1058,12 +1058,6 @@ void SynEditTextPainter::PaintLines() PaintEditAreas(areaList); } - if (nLineSelStart!=0 && nLineSelEnd!=0 - && nLineSelStart == nLineSelEnd) { - painter->setPen(edit->selectedBackground()); - int x =ColumnToXValue(nLineSelStart); - painter->drawLine(x,rcLine.top(),x,rcLine.bottom()+1); - } // 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.