Commit 53a30715 authored by mae's avatar mae
Browse files

first steps for a refactoring overlay system.

As side effect we can now play a bit with visualizing extra selections
parent fbcd6708
...@@ -126,6 +126,7 @@ protected: ...@@ -126,6 +126,7 @@ protected:
} // namespace Internal } // namespace Internal
} // namespace TextEditor } // namespace TextEditor
ITextEditor *BaseTextEditor::openEditorAt(const QString &fileName, ITextEditor *BaseTextEditor::openEditorAt(const QString &fileName,
int line, int line,
int column, int column,
...@@ -174,6 +175,9 @@ BaseTextEditor::BaseTextEditor(QWidget *parent) ...@@ -174,6 +175,9 @@ BaseTextEditor::BaseTextEditor(QWidget *parent)
d->m_extraArea->setMouseTracking(true); d->m_extraArea->setMouseTracking(true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->m_overlay = new TextEditorOverlay(this);
d->m_searchResultOverlay = new TextEditorOverlay(this);
d->setupDocumentSignals(d->m_document); d->setupDocumentSignals(d->m_document);
d->setupDocumentSignals(d->m_document); d->setupDocumentSignals(d->m_document);
...@@ -988,17 +992,9 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) ...@@ -988,17 +992,9 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
#if 0 #if 0
case Qt::Key_sterling: { case Qt::Key_Dollar: {
d->m_overlay->setVisible(!d->m_overlay->isVisible());
static bool toggle = false; d->m_overlay->setCursor(textCursor());
if ((toggle = !toggle)) {
QList<BaseTextEditor::BlockRange> rangeList;
rangeList += BaseTextEditor::BlockRange(4, 12);
rangeList += BaseTextEditor::BlockRange(15, 19);
setIfdefedOutBlocks(rangeList);
} else {
setIfdefedOutBlocks(QList<BaseTextEditor::BlockRange>());
}
e->accept(); e->accept();
return; return;
...@@ -1716,7 +1712,7 @@ bool BaseTextEditor::viewportEvent(QEvent *event) ...@@ -1716,7 +1712,7 @@ bool BaseTextEditor::viewportEvent(QEvent *event)
void BaseTextEditor::resizeEvent(QResizeEvent *e) void BaseTextEditor::resizeEvent(QResizeEvent *e)
{ {
QPlainTextEdit::resizeEvent(e); QPlainTextEdit::resizeEvent(e);
QRect cr = viewport()->rect(); QRect cr = rect();
d->m_extraArea->setGeometry( d->m_extraArea->setGeometry(
QStyle::visualRect(layoutDirection(), cr, QStyle::visualRect(layoutDirection(), cr,
QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height()))); QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height())));
...@@ -1787,9 +1783,10 @@ QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const ...@@ -1787,9 +1783,10 @@ QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const
return QTextBlock(); return QTextBlock();
} }
void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block, void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block)
QVector<QTextLayout::FormatRange> *selections)
{ {
m_searchResultOverlay->clear();
if (m_searchExpr.isEmpty()) if (m_searchExpr.isEmpty())
return; return;
...@@ -1809,11 +1806,9 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block, ...@@ -1809,11 +1806,9 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
if (m_findScope.isNull() if (m_findScope.isNull()
|| (block.position() + idx >= m_findScope.selectionStart() || (block.position() + idx >= m_findScope.selectionStart()
&& block.position() + idx + l <= m_findScope.selectionEnd())) { && block.position() + idx + l <= m_findScope.selectionEnd())) {
QTextLayout::FormatRange selection; m_searchResultOverlay->addOverlaySelection(block.position() + idx,
selection.start = idx; block.position() + idx + l,
selection.length = l; m_searchResultFormat.background().color());
selection.format = m_searchResultFormat;
selections->append(selection);
} }
} }
} }
...@@ -2034,7 +2029,10 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2034,7 +2029,10 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
QTextBlock visibleCollapsedBlock; QTextBlock visibleCollapsedBlock;
QPointF visibleCollapsedBlockOffset; QPointF visibleCollapsedBlockOffset;
QTextLayout *cursor_layout = 0;
QPointF cursor_offset;
int cursor_cpos = 0;
QPen cursor_pen;
while (block.isValid()) { while (block.isValid()) {
...@@ -2130,7 +2128,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2130,7 +2128,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
selections.append(o); selections.append(o);
} }
} }
d->highlightSearchResults(block, &selections); d->highlightSearchResults(block);
selections += prioritySelections; selections += prioritySelections;
bool drawCursor = ((editable || true) // we want the cursor in read-only mode bool drawCursor = ((editable || true) // we want the cursor in read-only mode
...@@ -2154,6 +2152,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2154,6 +2152,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
layout->draw(&painter, offset, selections, er); layout->draw(&painter, offset, selections, er);
d->m_searchResultOverlay->paint(&painter, er);
if ((drawCursor && !drawCursorAsBlock) if ((drawCursor && !drawCursorAsBlock)
|| (editable && context.cursorPosition < -1 || (editable && context.cursorPosition < -1
...@@ -2163,8 +2162,12 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2163,8 +2162,12 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
cpos = layout->preeditAreaPosition() - (cpos + 2); cpos = layout->preeditAreaPosition() - (cpos + 2);
else else
cpos -= blpos; cpos -= blpos;
layout->drawCursor(&painter, offset, cpos, cursorWidth()); cursor_layout = layout;
cursor_offset = offset;
cursor_cpos = cpos;
cursor_pen = painter.pen();
} }
} }
offset.ry() += r.height(); offset.ry() += r.height();
...@@ -2355,8 +2358,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2355,8 +2358,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset); QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset);
QTextLayout *layout = b.layout(); QTextLayout *layout = b.layout();
QVector<QTextLayout::FormatRange> selections; QVector<QTextLayout::FormatRange> selections;
d->highlightSearchResults(b, &selections); d->highlightSearchResults(b);
layout->draw(&painter, visibleCollapsedBlockOffset, selections, er); layout->draw(&painter, visibleCollapsedBlockOffset, selections, er);
d->m_searchResultOverlay->paint(&painter, er);
b.setVisible(false); // restore previous state b.setVisible(false); // restore previous state
visibleCollapsedBlockOffset.ry() += r.height(); visibleCollapsedBlockOffset.ry() += r.height();
...@@ -2378,6 +2382,17 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) ...@@ -2378,6 +2382,17 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
painter.setPen(QPen(col, 0)); painter.setPen(QPen(col, 0));
painter.drawLine(QPointF(lineX, 0), QPointF(lineX, viewport()->height())); painter.drawLine(QPointF(lineX, 0), QPointF(lineX, viewport()->height()));
} }
if (d->m_overlay && d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
// draw the cursor last, on top of everything
if (cursor_layout) {
painter.setPen(cursor_pen);
cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
}
} }
QWidget *BaseTextEditor::extraArea() const QWidget *BaseTextEditor::extraArea() const
...@@ -2821,16 +2836,20 @@ void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block) ...@@ -2821,16 +2836,20 @@ void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block)
static bool blockRecursion = false; static bool blockRecursion = false;
if (blockRecursion) if (blockRecursion)
return; return;
if (block.previous().isValid() && block.userState() != block.previous().userState()) { blockRecursion = true;
if (d->m_overlay->isVisible()) {
/* an overlay might draw outside the block bounderies, force
complete viewport update */
viewport()->update();
} else if (block.previous().isValid() && block.userState() != block.previous().userState()) {
/* The syntax highlighting state changes. This opens up for /* The syntax highlighting state changes. This opens up for
the possibility that the paragraph has braces that support the possibility that the paragraph has braces that support
code folding. In this case, do the save thing and also code folding. In this case, do the save thing and also
update the previous block, which might contain a collapse update the previous block, which might contain a collapse
box which now is invalid.*/ box which now is invalid.*/
blockRecursion = true;
emit requestBlockUpdate(block.previous()); emit requestBlockUpdate(block.previous());
blockRecursion = false;
} }
blockRecursion = false;
} }
void BaseTextEditor::timerEvent(QTimerEvent *e) void BaseTextEditor::timerEvent(QTimerEvent *e)
...@@ -4156,10 +4175,22 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe ...@@ -4156,10 +4175,22 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe
return; return;
d->m_extraSelections[kind] = selections; d->m_extraSelections[kind] = selections;
if (kind == CodeSemanticsSelection) {
d->m_overlay->clear();
foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
d->m_overlay->addOverlaySelection(selection.cursor, selection.format.background().color());
}
d->m_overlay->setVisible(!d->m_overlay->isEmpty());
} else {
QList<QTextEdit::ExtraSelection> all; QList<QTextEdit::ExtraSelection> all;
for (int i = 0; i < NExtraSelectionKinds; ++i) for (int i = 0; i < NExtraSelectionKinds; ++i) {
if (i == CodeSemanticsSelection)
continue;
all += d->m_extraSelections[i]; all += d->m_extraSelections[i];
}
QPlainTextEdit::setExtraSelections(all); QPlainTextEdit::setExtraSelections(all);
}
} }
QList<QTextEdit::ExtraSelection> BaseTextEditor::extraSelections(ExtraSelectionKind kind) const QList<QTextEdit::ExtraSelection> BaseTextEditor::extraSelections(ExtraSelectionKind kind) const
......
...@@ -52,6 +52,7 @@ namespace TextEditor { ...@@ -52,6 +52,7 @@ namespace TextEditor {
namespace Internal { namespace Internal {
class BaseTextEditorPrivate; class BaseTextEditorPrivate;
class TextEditorOverlay;
} }
class ITextMark; class ITextMark;
...@@ -445,6 +446,7 @@ private slots: ...@@ -445,6 +446,7 @@ private slots:
private: private:
Internal::BaseTextEditorPrivate *d; Internal::BaseTextEditorPrivate *d;
friend class Internal::BaseTextEditorPrivate; friend class Internal::BaseTextEditorPrivate;
friend class Internal::TextEditorOverlay;
public: public:
QWidget *extraArea() const; QWidget *extraArea() const;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#define BASETEXTEDITOR_P_H #define BASETEXTEDITOR_P_H
#include "basetexteditor.h" #include "basetexteditor.h"
#include "texteditoroverlay.h"
#include <texteditor/fontsettings.h> #include <texteditor/fontsettings.h>
#include <QtCore/QBasicTimer> #include <QtCore/QBasicTimer>
...@@ -183,6 +184,9 @@ public: ...@@ -183,6 +184,9 @@ public:
int extraAreaHighlightCollapseBlockNumber; int extraAreaHighlightCollapseBlockNumber;
int extraAreaHighlightCollapseColumn; int extraAreaHighlightCollapseColumn;
TextEditorOverlay *m_overlay;
TextEditorOverlay *m_searchResultOverlay;
QBasicTimer collapsedBlockTimer; QBasicTimer collapsedBlockTimer;
int visibleCollapsedBlockNumber; int visibleCollapsedBlockNumber;
int suggestedVisibleCollapsedBlockNumber; int suggestedVisibleCollapsedBlockNumber;
...@@ -215,8 +219,7 @@ public: ...@@ -215,8 +219,7 @@ public:
QTextCharFormat m_searchScopeFormat; QTextCharFormat m_searchScopeFormat;
QTextCharFormat m_currentLineFormat; QTextCharFormat m_currentLineFormat;
QTextCharFormat m_currentLineNumberFormat; QTextCharFormat m_currentLineNumberFormat;
void highlightSearchResults(const QTextBlock &block, void highlightSearchResults(const QTextBlock &block);
QVector<QTextLayout::FormatRange> *selections);
BaseTextEditorEditable *m_editable; BaseTextEditorEditable *m_editable;
......
...@@ -296,9 +296,9 @@ QColor FormatDescription::background() const ...@@ -296,9 +296,9 @@ QColor FormatDescription::background() const
const QPalette palette = QApplication::palette(); const QPalette palette = QApplication::palette();
return palette.color(QPalette::Highlight); return palette.color(QPalette::Highlight);
} else if (m_name == QLatin1String(Constants::C_OCCURRENCES)) { } else if (m_name == QLatin1String(Constants::C_OCCURRENCES)) {
return QColor(220, 220, 220); return QColor(180, 180, 180);
} else if (m_name == QLatin1String(Constants::C_OCCURRENCES_RENAME)) { } else if (m_name == QLatin1String(Constants::C_OCCURRENCES_RENAME)) {
return QColor(255, 200, 200); return QColor(255, 180, 180);
} else if (m_name == QLatin1String(Constants::C_DISABLED_CODE)) { } else if (m_name == QLatin1String(Constants::C_DISABLED_CODE)) {
return QColor(239, 239, 239); return QColor(239, 239, 239);
} }
......
...@@ -29,7 +29,9 @@ SOURCES += texteditorplugin.cpp \ ...@@ -29,7 +29,9 @@ SOURCES += texteditorplugin.cpp \
findincurrentfile.cpp \ findincurrentfile.cpp \
colorscheme.cpp \ colorscheme.cpp \
colorschemeedit.cpp \ colorschemeedit.cpp \
itexteditor.cpp itexteditor.cpp \
texteditoroverlay.cpp
HEADERS += texteditorplugin.h \ HEADERS += texteditorplugin.h \
textfilewizard.h \ textfilewizard.h \
plaintexteditor.h \ plaintexteditor.h \
...@@ -61,7 +63,10 @@ HEADERS += texteditorplugin.h \ ...@@ -61,7 +63,10 @@ HEADERS += texteditorplugin.h \
codecselector.h \ codecselector.h \
findincurrentfile.h \ findincurrentfile.h \
colorscheme.h \ colorscheme.h \
colorschemeedit.h colorschemeedit.h \
texteditoroverlay.h
FORMS += behaviorsettingspage.ui \ FORMS += behaviorsettingspage.ui \
displaysettingspage.ui \ displaysettingspage.ui \
fontsettingspage.ui \ fontsettingspage.ui \
......
#include <QtGui/QPainter>
#include "texteditoroverlay.h"
#include <QDebug>
using namespace TextEditor;
using namespace TextEditor::Internal;
TextEditorOverlay::TextEditorOverlay(BaseTextEditor *editor)
:QObject(editor) {
m_visible = false;
m_borderWidth = 1;
m_editor = editor;
m_viewport = editor->viewport();
}
void TextEditorOverlay::update()
{
if (m_visible)
m_viewport->update();
}
void TextEditorOverlay::setVisible(bool b)
{
if (m_visible == b)
return;
m_visible = b;
update();
}
void TextEditorOverlay::clear()
{
m_selections.clear();
update();
}
void TextEditorOverlay::addOverlaySelection(int begin, int end, const QColor &color)
{
if (end <= begin)
return;
QTextDocument *document = m_editor->document();
OverlaySelection selection;
selection.m_color = color;
selection.m_cursor_begin = QTextCursor(document);
selection.m_cursor_begin.setPosition(begin);
selection.m_cursor_end = QTextCursor(document);
selection.m_cursor_end.setPosition(end);
m_selections += selection;
update();
}
void TextEditorOverlay::addOverlaySelection(const QTextCursor &cursor, const QColor &color)
{
if (!cursor.hasSelection())
return;
addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), color);
}
QRect TextEditorOverlay::rect() const
{
return m_viewport->rect();
}
void TextEditorOverlay::paintSelection(QPainter *painter, const QTextCursor &begin, const QTextCursor &end, const QColor &color)
{
if (begin.isNull() || end.isNull() || begin.position() > end.position())
return;
QPointF offset = m_editor->contentOffset();
QRect viewportRect = rect();
QTextDocument *document = m_editor->document();
if (m_editor->blockBoundingGeometry(begin.block()).translated(offset).top() > viewportRect.bottom() + 10
|| m_editor->blockBoundingGeometry(end.block()).translated(offset).bottom() < viewportRect.top() - 10
)
return; // nothing of the selection is visible
QTextBlock block = begin.block();
bool inSelection = false;
QVector<QRectF> selection;
for (; block.isValid() && block.blockNumber() <= end.blockNumber(); block = block.next()) {
if (! block.isVisible())
continue;
const QRectF blockGeometry = m_editor->blockBoundingGeometry(block);
QTextLayout *blockLayout = block.layout();
QTextLine line = blockLayout->lineAt(0);
int beginChar = 0;
if (!inSelection) {
beginChar = begin.position() - begin.block().position();
line = blockLayout->lineForTextPosition(beginChar);
inSelection = true;
} else {
while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace())
++beginChar;
if (beginChar == block.length())
beginChar = 0;
}
int lastLine = blockLayout->lineCount()-1;
int endChar = -1;
if (block == end.block()) {
endChar = end.position() - end.block().position();
lastLine = blockLayout->lineForTextPosition(endChar).lineNumber();
inSelection = false;
} else {
endChar = block.length();
while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace())
--endChar;
}
QRectF lineRect = line.naturalTextRect();
if (beginChar < endChar) {
lineRect.setLeft(line.cursorToX(beginChar));
if (line.lineNumber() == lastLine)
lineRect.setRight(line.cursorToX(endChar));
selection += lineRect.translated(blockGeometry.topLeft());
for (int lineIndex = line.lineNumber()+1; lineIndex <= lastLine; ++lineIndex) {
line = blockLayout->lineAt(lineIndex);
lineRect = line.naturalTextRect();
if (lineIndex == lastLine)
lineRect.setRight(line.cursorToX(endChar));
selection += lineRect.translated(blockGeometry.topLeft());
}
} else { // empty lines
if (!selection.isEmpty())
lineRect.setLeft(selection.last().left());
lineRect.setRight(lineRect.left() + 16);
selection += lineRect.translated(blockGeometry.topLeft());
}
if (!inSelection)
break;
if (blockGeometry.translated(offset).y() > 2*viewportRect.height())
break;
}
if (selection.isEmpty())
return;
QVector<QPointF> points;
const int margin = m_borderWidth/2;
points += (selection.at(0).topLeft() + selection.at(0).topRight()) / 2 + QPointF(0, -margin);
points += selection.at(0).topRight() + QPointF(margin+1, -margin);
points += selection.at(0).bottomRight() + QPointF(margin+1, 0);
for(int i = 1; i < selection.count()-1; ++i) {
#define MAX3(a,b,c) qMax(a, qMax(b,c))
qreal x = MAX3(selection.at(i-1).right(),
selection.at(i).right(),
selection.at(i+1).right()) + margin;
points += QPointF(x+1, selection.at(i).top());
points += QPointF(x+1, selection.at(i).bottom()+1);
}
points += selection.at(selection.count()-1).topRight() + QPointF(margin+1, 0);
points += selection.at(selection.count()-1).bottomRight() + QPointF(margin+1, margin+1);
points += selection.at(selection.count()-1).bottomLeft() + QPointF(-margin, margin+1);
points += selection.at(selection.count()-1).topLeft() + QPointF(-margin, 0);
for(int i = selection.count()-2; i > 0; --i) {
#define MIN3(a,b,c) qMin(a, qMin(b,c))
qreal x = MIN3(selection.at(i-1).left(),
selection.at(i).left(),
selection.at(i+1).left()) - margin;
points += QPointF(x, selection.at(i).bottom()+1);
points += QPointF(x, selection.at(i).top());
}
points += selection.at(0).bottomLeft() + QPointF(-margin, 1);
points += selection.at(0).topLeft() + QPointF(-margin, -margin);
QPainterPath path;
const int corner = 4;
path.moveTo(points.at(0));
points += points.at(0);
QPointF previous = points.at(0);
for (int i = 1; i < points.size(); ++i) {
QPointF point = points.at(i);
if (point.y() == previous.y() && qAbs(point.x() - previous.x()) > 2*corner) {
QPointF tmp = QPointF(previous.x() + corner * ((point.x() > previous.x())?1:-1), previous.y());
path.quadTo(previous, tmp);
previous = tmp;
i--;
continue;
} else if (point.x() == previous.x() && qAbs(point.y() - previous.y()) > 2*corner) {