Commit 53a30715 authored by mae's avatar mae

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:
} // namespace Internal
} // namespace TextEditor
ITextEditor *BaseTextEditor::openEditorAt(const QString &fileName,
int line,
int column,
......@@ -174,6 +175,9 @@ BaseTextEditor::BaseTextEditor(QWidget *parent)
d->m_extraArea->setMouseTracking(true);
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);
......@@ -988,18 +992,10 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
#if 0
case Qt::Key_sterling: {
static bool toggle = false;
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();
case Qt::Key_Dollar: {
d->m_overlay->setVisible(!d->m_overlay->isVisible());
d->m_overlay->setCursor(textCursor());
e->accept();
return;
} break;
......@@ -1716,7 +1712,7 @@ bool BaseTextEditor::viewportEvent(QEvent *event)
void BaseTextEditor::resizeEvent(QResizeEvent *e)
{
QPlainTextEdit::resizeEvent(e);
QRect cr = viewport()->rect();
QRect cr = rect();
d->m_extraArea->setGeometry(
QStyle::visualRect(layoutDirection(), cr,
QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height())));
......@@ -1787,9 +1783,10 @@ QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const
return QTextBlock();
}
void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
QVector<QTextLayout::FormatRange> *selections)
void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block)
{
m_searchResultOverlay->clear();
if (m_searchExpr.isEmpty())
return;
......@@ -1809,11 +1806,9 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
if (m_findScope.isNull()
|| (block.position() + idx >= m_findScope.selectionStart()
&& block.position() + idx + l <= m_findScope.selectionEnd())) {
QTextLayout::FormatRange selection;
selection.start = idx;
selection.length = l;
selection.format = m_searchResultFormat;
selections->append(selection);
m_searchResultOverlay->addOverlaySelection(block.position() + idx,
block.position() + idx + l,
m_searchResultFormat.background().color());
}
}
}
......@@ -2034,7 +2029,10 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
QTextBlock visibleCollapsedBlock;
QPointF visibleCollapsedBlockOffset;
QTextLayout *cursor_layout = 0;
QPointF cursor_offset;
int cursor_cpos = 0;
QPen cursor_pen;
while (block.isValid()) {
......@@ -2130,7 +2128,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
selections.append(o);
}
}
d->highlightSearchResults(block, &selections);
d->highlightSearchResults(block);
selections += prioritySelections;
bool drawCursor = ((editable || true) // we want the cursor in read-only mode
......@@ -2154,6 +2152,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
layout->draw(&painter, offset, selections, er);
d->m_searchResultOverlay->paint(&painter, er);
if ((drawCursor && !drawCursorAsBlock)
|| (editable && context.cursorPosition < -1
......@@ -2163,8 +2162,12 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
cpos = layout->preeditAreaPosition() - (cpos + 2);
else
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();
......@@ -2355,8 +2358,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
QRectF r = blockBoundingRect(b).translated(visibleCollapsedBlockOffset);
QTextLayout *layout = b.layout();
QVector<QTextLayout::FormatRange> selections;
d->highlightSearchResults(b, &selections);
d->highlightSearchResults(b);
layout->draw(&painter, visibleCollapsedBlockOffset, selections, er);
d->m_searchResultOverlay->paint(&painter, er);
b.setVisible(false); // restore previous state
visibleCollapsedBlockOffset.ry() += r.height();
......@@ -2378,6 +2382,17 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
painter.setPen(QPen(col, 0));
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
......@@ -2821,16 +2836,20 @@ void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block)
static bool blockRecursion = false;
if (blockRecursion)
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 possibility that the paragraph has braces that support
code folding. In this case, do the save thing and also
update the previous block, which might contain a collapse
box which now is invalid.*/
blockRecursion = true;
emit requestBlockUpdate(block.previous());
blockRecursion = false;
}
blockRecursion = false;
}
void BaseTextEditor::timerEvent(QTimerEvent *e)
......@@ -4156,10 +4175,22 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe
return;
d->m_extraSelections[kind] = selections;
QList<QTextEdit::ExtraSelection> all;
for (int i = 0; i < NExtraSelectionKinds; ++i)
all += d->m_extraSelections[i];
QPlainTextEdit::setExtraSelections(all);
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;
for (int i = 0; i < NExtraSelectionKinds; ++i) {
if (i == CodeSemanticsSelection)
continue;
all += d->m_extraSelections[i];
}
QPlainTextEdit::setExtraSelections(all);
}
}
QList<QTextEdit::ExtraSelection> BaseTextEditor::extraSelections(ExtraSelectionKind kind) const
......
......@@ -52,6 +52,7 @@ namespace TextEditor {
namespace Internal {
class BaseTextEditorPrivate;
class TextEditorOverlay;
}
class ITextMark;
......@@ -445,6 +446,7 @@ private slots:
private:
Internal::BaseTextEditorPrivate *d;
friend class Internal::BaseTextEditorPrivate;
friend class Internal::TextEditorOverlay;
public:
QWidget *extraArea() const;
......
......@@ -31,6 +31,7 @@
#define BASETEXTEDITOR_P_H
#include "basetexteditor.h"
#include "texteditoroverlay.h"
#include <texteditor/fontsettings.h>
#include <QtCore/QBasicTimer>
......@@ -183,6 +184,9 @@ public:
int extraAreaHighlightCollapseBlockNumber;
int extraAreaHighlightCollapseColumn;
TextEditorOverlay *m_overlay;
TextEditorOverlay *m_searchResultOverlay;
QBasicTimer collapsedBlockTimer;
int visibleCollapsedBlockNumber;
int suggestedVisibleCollapsedBlockNumber;
......@@ -215,8 +219,7 @@ public:
QTextCharFormat m_searchScopeFormat;
QTextCharFormat m_currentLineFormat;
QTextCharFormat m_currentLineNumberFormat;
void highlightSearchResults(const QTextBlock &block,
QVector<QTextLayout::FormatRange> *selections);
void highlightSearchResults(const QTextBlock &block);
BaseTextEditorEditable *m_editable;
......
......@@ -296,9 +296,9 @@ QColor FormatDescription::background() const
const QPalette palette = QApplication::palette();
return palette.color(QPalette::Highlight);
} 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)) {
return QColor(255, 200, 200);
return QColor(255, 180, 180);
} else if (m_name == QLatin1String(Constants::C_DISABLED_CODE)) {
return QColor(239, 239, 239);
}
......
......@@ -29,7 +29,9 @@ SOURCES += texteditorplugin.cpp \
findincurrentfile.cpp \
colorscheme.cpp \
colorschemeedit.cpp \
itexteditor.cpp
itexteditor.cpp \
texteditoroverlay.cpp
HEADERS += texteditorplugin.h \
textfilewizard.h \
plaintexteditor.h \
......@@ -61,7 +63,10 @@ HEADERS += texteditorplugin.h \
codecselector.h \
findincurrentfile.h \
colorscheme.h \
colorschemeedit.h
colorschemeedit.h \
texteditoroverlay.h
FORMS += behaviorsettingspage.ui \
displaysettingspage.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) {
QPointF tmp = QPointF(previous.x(), previous.y() + corner * ((point.y() > previous.y())?1:-1));
path.quadTo(previous, tmp);
previous = tmp;
i--;
continue;
}
QPointF target = (previous + point) / 2;
path.quadTo(previous, target);
previous = points.at(i);
}
path.closeSubpath();
painter->save();
QPen pen(color, m_borderWidth);
painter->translate(-.5, -.5);
QColor brush = color;
brush.setAlpha(30);
painter->setRenderHint(QPainter::Antialiasing);
pen.setJoinStyle(Qt::RoundJoin);
painter->setPen(pen);
painter->setBrush(brush);
path.translate(offset);
painter->drawPath(path);
painter->restore();
}
void TextEditorOverlay::paint(QPainter *painter, const QRect &clip)
{
Q_UNUSED(clip);
for (int i = 0; i < m_selections.size(); ++i) {
const OverlaySelection &selection = m_selections.at(i);
paintSelection(painter,
selection.m_cursor_begin,
selection.m_cursor_end,
selection.m_color
);
}
}
#ifndef TEXTEDITOROVERLAY_H
#define TEXTEDITOROVERLAY_H
#include <QtGui/QWidget>
#include "basetexteditor.h"
namespace TextEditor {
namespace Internal {
struct TEXTEDITOR_EXPORT OverlaySelection {
QTextCursor m_cursor_begin;
QTextCursor m_cursor_end;
QColor m_color;
};
class TEXTEDITOR_EXPORT TextEditorOverlay : public QObject
{
Q_OBJECT
BaseTextEditor *m_editor;
QWidget *m_viewport;
QList<OverlaySelection> m_selections;
bool m_visible;
int m_borderWidth;
public:
TextEditorOverlay(BaseTextEditor *editor);
QRect rect() const;
void paint(QPainter *painter, const QRect &clip);
bool isVisible() const { return m_visible; }
void setVisible(bool b);
void setBorderWidth(int bw) {m_borderWidth = bw; }
void update();
void clear();
void addOverlaySelection(const QTextCursor &cursor, const QColor &color);
void addOverlaySelection(int begin, int end, const QColor &color);
inline bool isEmpty() const { return m_selections.isEmpty(); }
private:
void paintSelection(QPainter *painter, const QTextCursor &begin, const QTextCursor &end, const QColor &color);
};
} // namespace Internal
} // namespace TextEditor
#endif // TEXTEDITOROVERLAY_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment