Commit 6ef602d1 authored by mae's avatar mae

add basic support to show refactor markers

See BaseTextEditor::setRefactorMarkers(...). The markers support
a clicked signal via BaseTextEditor and tooltips. They feature
a cursor, but are always positioned at the end of the line (not block!).

As special gimmick they do extend the document width when they
are positioned outside the document area.
parent 038d943f
......@@ -512,4 +512,22 @@ void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold
setFolded(block, !unfold);
}
void BaseTextDocumentLayout::setRequiredWidth(int width)
{
int oldw = m_requiredWidth;
m_requiredWidth = width;
int dw = QPlainTextDocumentLayout::documentSize().width();
if (oldw > dw || width > dw)
emitDocumentSizeChanged();
}
QSizeF BaseTextDocumentLayout::documentSize() const
{
QSizeF size = QPlainTextDocumentLayout::documentSize();
size.setWidth(qMax((qreal)m_requiredWidth, size.width()));
return size;
}
......@@ -159,6 +159,11 @@ public:
void emitDocumentSizeChanged() { emit documentSizeChanged(documentSize()); }
int lastSaveRevision;
bool hasMarks;
int m_requiredWidth;
void setRequiredWidth(int width);
QSizeF documentSize() const;
};
} // namespace TextEditor
......
......@@ -81,6 +81,8 @@
#include <QtGui/QInputDialog>
#include <QtGui/QMenu>
//#define DO_FOO
using namespace TextEditor;
using namespace TextEditor::Internal;
......@@ -189,6 +191,7 @@ BaseTextEditor::BaseTextEditor(QWidget *parent)
d->m_overlay = new TextEditorOverlay(this);
d->m_snippetOverlay = new TextEditorOverlay(this);
d->m_searchResultOverlay = new TextEditorOverlay(this);
d->m_refactorOverlay = new RefactorOverlay(this);
d->setupDocumentSignals(d->m_document);
d->setupDocumentSignals(d->m_document);
......@@ -217,6 +220,10 @@ BaseTextEditor::BaseTextEditor(QWidget *parent)
// (void) new QShortcut(tr("F9"), this, SLOT(slotToggleMark()), 0, Qt::WidgetShortcut);
// (void) new QShortcut(tr("F11"), this, SLOT(slotToggleBlockVisible()));
#ifdef DO_FOO
(void) new QShortcut(tr("CTRL+D"), this, SLOT(doFoo()));
#endif
// parentheses matcher
d->m_formatRange = true;
......@@ -1610,12 +1617,10 @@ void BaseTextEditor::documentAboutToBeReloaded()
QPlainTextEdit::setExtraSelections(QList<QTextEdit::ExtraSelection>());
// clear all overlays
if (d->m_overlay)
d->m_overlay->clear();
if (d->m_snippetOverlay)
d->m_snippetOverlay->clear();
if (d->m_searchResultOverlay)
d->m_searchResultOverlay->clear();
d->m_overlay->clear();
d->m_snippetOverlay->clear();
d->m_searchResultOverlay->clear();
d->m_refactorOverlay->clear();
}
void BaseTextEditor::documentReloaded()
......@@ -1867,6 +1872,7 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
m_overlay(0),
m_snippetOverlay(0),
m_searchResultOverlay(0),
m_refactorOverlay(0),
visibleFoldedBlockNumber(-1),
suggestedVisibleFoldedBlockNumber(-1),
m_mouseOnFoldedMarker(false),
......@@ -2001,6 +2007,12 @@ bool BaseTextEditor::viewportEvent(QEvent *event)
return true; // eat tooltip event when control is pressed
const QPoint &pos = he->pos();
RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(pos);
if (refactorMarker.isValid() && !refactorMarker.tooltip.isEmpty()) {
QToolTip::showText(he->globalPos(), refactorMarker.tooltip, viewport(), refactorMarker.rect);
return true;
}
// Allow plugins to show tooltips
const QTextCursor &c = cursorForPosition(pos);
QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
......@@ -2763,10 +2775,10 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
}
if (d->m_overlay && d->m_overlay->isVisible())
if (d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
if (d->m_snippetOverlay && d->m_snippetOverlay->isVisible())
if (d->m_snippetOverlay->isVisible())
d->m_snippetOverlay->paint(&painter, e->rect());
if (!d->m_searchResultOverlay->isEmpty()) {
......@@ -2774,6 +2786,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
d->m_searchResultOverlay->clear();
}
if (!d->m_refactorOverlay->isEmpty())
d->m_refactorOverlay->paint(&painter, e->rect());
// draw the cursor last, on top of everything
if (cursor_layout) {
painter.setPen(cursor_pen);
......@@ -3373,11 +3388,13 @@ void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
d->foldedBlockTimer.start(40, this);
}
const RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
// Update the mouse cursor
if (collapsedBlock.isValid() && !d->m_mouseOnFoldedMarker) {
if ((collapsedBlock.isValid() || refactorMarker.isValid()) && !d->m_mouseOnFoldedMarker) {
d->m_mouseOnFoldedMarker = true;
viewport()->setCursor(Qt::PointingHandCursor);
} else if (!collapsedBlock.isValid() && d->m_mouseOnFoldedMarker) {
} else if (!collapsedBlock.isValid() && !refactorMarker.isValid() && d->m_mouseOnFoldedMarker) {
d->m_mouseOnFoldedMarker = false;
viewport()->setCursor(Qt::IBeamCursor);
}
......@@ -3420,10 +3437,16 @@ void BaseTextEditor::mousePressEvent(QMouseEvent *e)
viewport()->setCursor(Qt::IBeamCursor);
}
updateLink(e);
RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
if (refactorMarker.isValid()) {
qDebug() << "refactorMarkerClicked" << refactorMarker.cursor.position();
emit refactorMarkerClicked(refactorMarker);
} else {
updateLink(e);
if (d->m_currentLink.isValid())
d->m_linkPressed = true;
if (d->m_currentLink.isValid())
d->m_linkPressed = true;
}
}
#ifdef Q_OS_LINUX
......@@ -5388,3 +5411,25 @@ QString BaseTextEditorEditable::contextHelpId() const
e->textCursor().position());
return m_contextHelpId;
}
void BaseTextEditor::setRefactorMarkers(const Internal::RefactorMarkers &markers)
{
foreach (const Internal::RefactorMarker &marker, d->m_refactorOverlay->markers())
requestBlockUpdate(marker.cursor.block());
d->m_refactorOverlay->setMarkers(markers);
foreach (const Internal::RefactorMarker &marker, markers)
requestBlockUpdate(marker.cursor.block());
}
void BaseTextEditor::doFoo() {
#ifdef DO_FOO
qDebug() << Q_FUNC_INFO;
RefactorMarkers markers = d->m_refactorOverlay->markers();
RefactorMarker marker;
marker.tooltip = "Hello World";
marker.cursor = textCursor();
markers += marker;
setRefactorMarkers(markers);
#endif
}
......@@ -51,6 +51,10 @@ namespace TextEditor {
namespace Internal {
class BaseTextEditorPrivate;
class TextEditorOverlay;
class RefactorOverlay;
class RefactorMarker;
typedef QList<RefactorMarker> RefactorMarkers;
}
class ITextMarkable;
......@@ -315,6 +319,7 @@ private:
Internal::BaseTextEditorPrivate *d;
friend class Internal::BaseTextEditorPrivate;
friend class Internal::TextEditorOverlay;
friend class Internal::RefactorOverlay;
public:
QWidget *extraArea() const;
......@@ -348,6 +353,13 @@ public:
QList<QTextEdit::ExtraSelection> extraSelections(ExtraSelectionKind kind) const;
QString extraSelectionTooltip(int pos) const;
void setRefactorMarkers(const Internal::RefactorMarkers &markers);
signals:
void refactorMarkerClicked(const Internal::RefactorMarker &marker);
public:
struct BlockRange
{
BlockRange() : first(0), last(-1) {}
......@@ -516,6 +528,7 @@ private slots:
void _q_highlightBlocks();
void slotSelectionChanged();
void _q_animateUpdate(int position, QPointF lastPos, QRectF rect);
void doFoo();
};
......
......@@ -35,6 +35,7 @@
#include "displaysettings.h"
#include "texteditoroverlay.h"
#include "fontsettings.h"
#include "refactoroverlay.h"
#include <utils/changeset.h>
......@@ -199,6 +200,8 @@ public:
QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrenceRenameFormat;
RefactorOverlay *m_refactorOverlay;
QBasicTimer foldedBlockTimer;
int visibleFoldedBlockNumber;
int suggestedVisibleFoldedBlockNumber;
......
#include "refactoroverlay.h"
#include <QPainter>
#include <QTextBlock>
#include "basetextdocumentlayout.h"
#include <QDebug>
using namespace TextEditor::Internal;
RefactorOverlay::RefactorOverlay(BaseTextEditor *editor) :
QObject(editor),
m_editor(editor),
m_maxWidth(0),
m_icon(":/texteditor/images/refactormarker.png")
{
}
void RefactorOverlay::paint(QPainter *painter, const QRect &clip)
{
QTextBlock lastBlock;
int position = 0;
m_maxWidth = 0;
for (int i = 0; i < m_markers.size(); ++i) {
// position counts how many refactor markers are in a single block
if (m_markers.at(i).cursor.block() != lastBlock) {
lastBlock = m_markers.at(i).cursor.block();
position = 0;
} else {
position++;
}
paintMarker(m_markers.at(i), position, painter, clip);
}
if (BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(m_editor->document()->documentLayout())) {
documentLayout->setRequiredWidth(m_maxWidth);
}
}
RefactorMarker RefactorOverlay::markerAt(const QPoint &pos) const
{
QPointF offset = m_editor->contentOffset();
foreach(const RefactorMarker &marker, m_markers) {
if (marker.rect.translated(offset.toPoint()).contains(pos))
return marker;
}
return RefactorMarker();
}
void RefactorOverlay::paintMarker(const RefactorMarker& marker, int position,
QPainter *painter, const QRect &clip)
{
QPointF offset = m_editor->contentOffset();
QRectF geometry = m_editor->blockBoundingGeometry(marker.cursor.block()).translated(offset);
if (geometry.top() > clip.bottom() + 10 || geometry.bottom() < clip.top() - 10)
return; // marker not visible
QTextCursor cursor = marker.cursor;
cursor.movePosition(QTextCursor::EndOfLine);
QRect r = m_editor->cursorRect(cursor);
QIcon icon = marker.icon;
if (icon.isNull())
icon = m_icon;
QSize sz = icon.actualSize(QSize(INT_MAX, r.height()));
int x = r.right() + position * sz.width();
marker.rect = QRect(x, r.bottom() - sz.height(), sz.width(), sz.height()).translated(-offset.toPoint());
icon.paint(painter, marker.rect);
m_maxWidth = qMax((qreal)m_maxWidth, x + sz.width() - offset.x());
}
#ifndef REFACTOROVERLAY_H
#define REFACTOROVERLAY_H
#include "basetexteditor.h"
#include <QTextCursor>
#include <QObject>
#include <QIcon>
namespace TextEditor {
namespace Internal {
struct TEXTEDITOR_EXPORT RefactorMarker {
inline bool isValid() const { return !cursor.isNull(); }
QTextCursor cursor;
QString tooltip;
QIcon icon;
mutable QRect rect; // used to cache last drawing positin in document coordinates
QVariant data;
};
typedef QList<RefactorMarker> RefactorMarkers;
class TEXTEDITOR_EXPORT RefactorOverlay : public QObject
{
Q_OBJECT
public:
explicit RefactorOverlay(BaseTextEditor *editor);
bool isEmpty() const { return m_markers.isEmpty(); }
void paint(QPainter *painter, const QRect &clip);
void setMarkers(const RefactorMarkers &markers) { m_markers = markers; }
RefactorMarkers markers() const { return m_markers; }
void clear() { m_markers.clear(); }
RefactorMarker markerAt(const QPoint &pos) const;
private:
void paintMarker(const RefactorMarker& marker, int position, QPainter *painter, const QRect &clip);
RefactorMarkers m_markers;
BaseTextEditor *m_editor;
int m_maxWidth;
QIcon m_icon;
};
}
}
#endif // REFACTOROVERLAY_H
......@@ -59,7 +59,8 @@ SOURCES += texteditorplugin.cpp \
generichighlighter/managedefinitionsdialog.cpp \
generichighlighter/highlightdefinitionmetadata.cpp \
generichighlighter/definitiondownloader.cpp \
refactoringchanges.cpp
refactoringchanges.cpp \
refactoroverlay.cpp
HEADERS += texteditorplugin.h \
textfilewizard.h \
......@@ -121,7 +122,8 @@ HEADERS += texteditorplugin.h \
generichighlighter/managedefinitionsdialog.h \
generichighlighter/highlightdefinitionmetadata.h \
generichighlighter/definitiondownloader.h \
refactoringchanges.h
refactoringchanges.h \
refactoroverlay.h
FORMS += behaviorsettingspage.ui \
displaysettingspage.ui \
......
<RCC>
<qresource prefix="/texteditor" >
<qresource prefix="/texteditor">
<file>images/finddocuments.png</file>
<file>images/finddirectory.png</file>
<file>TextEditor.mimetypes.xml</file>
<file>images/refactormarker.png</file>
</qresource>
</RCC>
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