Commit 4959230c authored by mae's avatar mae

beginnings of fancy snippet editing support

parent 701b3e0a
......@@ -175,6 +175,7 @@ BaseTextEditor::BaseTextEditor(QWidget *parent)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
d->m_overlay = new TextEditorOverlay(this);
d->m_snippetOverlay = new TextEditorOverlay(this);
d->m_searchResultOverlay = new TextEditorOverlay(this);
d->setupDocumentSignals(d->m_document);
......@@ -1080,6 +1081,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
case Qt::Key_Tab:
case Qt::Key_Backtab: {
if (ro) break;
if (d->m_snippetOverlay->isVisible() && !d->m_snippetOverlay->isEmpty()) {
d->snippetTabOrBacktab(e->key() == Qt::Key_Tab);
e->accept();
return;
}
QTextCursor cursor = textCursor();
int newPosition;
if (d->m_document->tabSettings().tabShouldIndent(document(), cursor, &newPosition)) {
......@@ -1172,6 +1178,12 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
}
}
if (e->key() == Qt::Key_H && e->modifiers() == Qt::ControlModifier) {
universalHelper();
e->accept();
return;
}
if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
QPlainTextEdit::keyPressEvent(e);
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
......@@ -1189,6 +1201,9 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
}
}
if (d->m_snippetOverlay->isVisible())
d->snippetCheckCursor(cursor);
bool doEditBlock = !(electricChar.isNull() && autoText.isEmpty());
if (doEditBlock)
cursor.beginEditBlock();
......@@ -1221,6 +1236,28 @@ skip_event:
delete e;
}
void BaseTextEditor::universalHelper()
{
QList<QTextEdit::ExtraSelection> selections;
QTextCursor cursor = textCursor();
cursor.beginEditBlock();
int pos = cursor.position();
cursor.insertText("if (condition){\n \n }\n");
QTextCursor c = cursor;
c.setPosition(pos + 3); // condition-1;
c.setPosition(c.position() + 10, QTextCursor::KeepAnchor);
QTextEdit::ExtraSelection selection;
selection.cursor = c;
selection.format.setBackground(Qt::darkCyan);
selections.append(selection);
c.setPosition(pos + 24); // contents
selection.cursor = c;
selection.format.setBackground(Qt::darkMagenta);
selections.append(selection);
cursor.endEditBlock();
setExtraSelections(BaseTextEditor::SnippetPlaceholderSelection, selections);
}
void BaseTextEditor::setTextCursor(const QTextCursor &cursor)
{
// workaround for QTextControl bug
......@@ -1611,6 +1648,55 @@ void BaseTextEditorPrivate::setupDocumentSignals(BaseTextDocument *document)
q->slotUpdateExtraAreaWidth();
}
void BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
{
if (cursor.hasSelection())
return;
if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
return;
if (!m_snippetOverlay->hasCursorInSelection(cursor)) {
m_snippetOverlay->setVisible(false);
m_snippetOverlay->clear();
}
}
void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
{
if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
return;
QTextCursor cursor = q->textCursor();
OverlaySelection final;
if (forward) {
for (int i = 0; i < m_snippetOverlay->m_selections.count(); ++i){
const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i);
if (selection.m_cursor_begin.position() > cursor.position()) {
final = selection;
break;
}
}
} else {
for (int i = m_snippetOverlay->m_selections.count()-1; i >= 0; --i){
const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i);
if (selection.m_cursor_end.position() < cursor.position()) {
final = selection;
break;
}
}
}
if (final.m_cursor_begin.isNull())
final = forward ? m_snippetOverlay->m_selections.first() : m_snippetOverlay->m_selections.last();
if (final.m_cursor_begin.position() == final.m_cursor_end.position()) { // empty tab stop
cursor.setPosition(final.m_cursor_end.position());
} else {
cursor.setPosition(final.m_cursor_begin.position()+1);
cursor.setPosition(final.m_cursor_end.position(), QTextCursor::KeepAnchor);
}
q->setTextCursor(cursor);
}
bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses)
{
return closeCollapseAtPos(parentheses) >= 0;
......@@ -1906,9 +1992,10 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
overlay->addOverlaySelection(blockPosition + idx,
blockPosition + idx + l,
m_searchResultFormat.background().color().darker(120),
QColor(), false,
QColor(),
(idx == cursor.selectionStart() - blockPosition
&& idx + l == cursor.selectionEnd() - blockPosition));
&& idx + l == cursor.selectionEnd() - blockPosition)?
TextEditorOverlay::DropShadow : 0);
}
}
......@@ -2516,6 +2603,9 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
if (d->m_overlay && d->m_overlay->isVisible())
d->m_overlay->paint(&painter, e->rect());
if (d->m_snippetOverlay && d->m_snippetOverlay->isVisible())
d->m_snippetOverlay->paint(&painter, e->rect());
if (!d->m_searchResultOverlay->isEmpty()) {
d->m_searchResultOverlay->paint(&painter, e->rect());
......@@ -4578,14 +4668,22 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe
d->m_overlay->addOverlaySelection(selection.cursor,
selection.format.background().color(),
selection.format.background().color(),
true);
TextEditorOverlay::LockSize);
}
d->m_overlay->setVisible(!d->m_overlay->isEmpty());
} else if (kind == SnippetPlaceholderSelection) {
d->m_snippetOverlay->clear();
foreach (const QTextEdit::ExtraSelection &selection, d->m_extraSelections[kind]) {
d->m_snippetOverlay->addOverlaySelection(selection.cursor,
selection.format.background().color(),
selection.format.background().color(),
TextEditorOverlay::ExpandBegin);
}
d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty());
} else {
QList<QTextEdit::ExtraSelection> all;
for (int i = 0; i < NExtraSelectionKinds; ++i) {
if (i == CodeSemanticsSelection)
if (i == CodeSemanticsSelection || i == SnippetPlaceholderSelection)
continue;
all += d->m_extraSelections[i];
}
......
......@@ -482,6 +482,7 @@ public:
UnusedSymbolSelection,
FakeVimSelection,
OtherSelection,
SnippetPlaceholderSelection,
NExtraSelectionKinds
};
void setExtraSelections(ExtraSelectionKind kind, const QList<QTextEdit::ExtraSelection> &selections);
......@@ -635,6 +636,8 @@ private:
void showLink(const Link &);
void clearLink();
void universalHelper(); // test function for development
// parentheses matcher
private slots:
void _q_matchParentheses();
......
......@@ -194,7 +194,10 @@ public:
int extraAreaHighlightCollapseColumn;
TextEditorOverlay *m_overlay;
TextEditorOverlay *m_snippetOverlay;
TextEditorOverlay *m_searchResultOverlay;
void snippetCheckCursor(const QTextCursor &cursor);
void snippetTabOrBacktab(bool forward);
QBasicTimer collapsedBlockTimer;
int visibleCollapsedBlockNumber;
......
......@@ -57,8 +57,8 @@ void TextEditorOverlay::setVisible(bool b)
{
if (m_visible == b)
return;
m_visible = b;
update();
m_visible = b;
}
void TextEditorOverlay::clear()
......@@ -71,8 +71,7 @@ void TextEditorOverlay::clear()
void TextEditorOverlay::addOverlaySelection(int begin, int end,
const QColor &fg, const QColor &bg,
bool lockSize,
bool dropShadow)
uint overlaySelectionFlags)
{
if (end < begin)
return;
......@@ -83,26 +82,31 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end,
selection.m_fg = fg;
selection.m_bg = bg;
if (overlaySelectionFlags & ExpandBegin) {
if (begin > 0 && begin < end) { // not empty
selection.m_expandBegin = true;
}
}
selection.m_cursor_begin = QTextCursor(document->docHandle(), begin);
selection.m_cursor_end = QTextCursor(document->docHandle(), end);
if (lockSize)
if (overlaySelectionFlags & LockSize)
selection.m_fixedLength = (end - begin);
selection.m_dropShadow = dropShadow;
if (dropShadow)
m_selections.append(selection);
else
m_selections.prepend(selection);
selection.m_dropShadow = (overlaySelectionFlags & DropShadow);
m_selections.append(selection);
update();
}
void TextEditorOverlay::addOverlaySelection(const QTextCursor &cursor,
const QColor &fg, const QColor &bg, bool lockSize)
const QColor &fg, const QColor &bg,
uint overlaySelectionFlags)
{
addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, lockSize);
addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, overlaySelectionFlags);
}
QRect TextEditorOverlay::rect() const
......@@ -296,13 +300,18 @@ void TextEditorOverlay::paintSelection(QPainter *painter,
const OverlaySelection &selection)
{
const QTextCursor &begin = selection.m_cursor_begin;
QTextCursor begin = selection.m_cursor_begin;
if (selection.m_expandBegin)
begin.setPosition(begin.position() + 1);
const QTextCursor &end= selection.m_cursor_end;
const QColor &fg = selection.m_fg;
const QColor &bg = selection.m_bg;
if (begin.isNull() || end.isNull() || begin.position() > end.position())
if (begin.isNull()
|| end.isNull()
|| begin.position() > end.position())
return;
QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect());
......@@ -377,8 +386,21 @@ void TextEditorOverlay::fillSelection(QPainter *painter,
void TextEditorOverlay::paint(QPainter *painter, const QRect &clip)
{
Q_UNUSED(clip);
for (int i = 0; i < m_selections.size(); ++i) {
for (int i = m_selections.size()-1; i >= 0; --i) {
const OverlaySelection &selection = m_selections.at(i);
if (selection.m_dropShadow)
continue;
if (selection.m_fixedLength >= 0
&& selection.m_cursor_end.position() - selection.m_cursor_begin.position()
!= selection.m_fixedLength)
continue;
paintSelection(painter, selection);
}
for (int i = m_selections.size()-1; i >= 0; --i) {
const OverlaySelection &selection = m_selections.at(i);
if (!selection.m_dropShadow)
continue;
if (selection.m_fixedLength >= 0
&& selection.m_cursor_end.position() - selection.m_cursor_begin.position()
!= selection.m_fixedLength)
......@@ -391,8 +413,21 @@ void TextEditorOverlay::paint(QPainter *painter, const QRect &clip)
void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect &clip)
{
Q_UNUSED(clip);
for (int i = 0; i < m_selections.size(); ++i) {
for (int i = m_selections.size()-1; i >= 0; --i) {
const OverlaySelection &selection = m_selections.at(i);
if (selection.m_dropShadow)
continue;
if (selection.m_fixedLength >= 0
&& selection.m_cursor_end.position() - selection.m_cursor_begin.position()
!= selection.m_fixedLength)
continue;
fillSelection(painter, selection, color);
}
for (int i = m_selections.size()-1; i >= 0; --i) {
const OverlaySelection &selection = m_selections.at(i);
if (!selection.m_dropShadow)
continue;
if (selection.m_fixedLength >= 0
&& selection.m_cursor_end.position() - selection.m_cursor_begin.position()
!= selection.m_fixedLength)
......@@ -402,3 +437,13 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect
}
}
bool TextEditorOverlay::hasCursorInSelection(const QTextCursor &cursor) const
{
for (int i = 0; i < m_selections.size(); ++i) {
const OverlaySelection &selection = m_selections.at(i);
if (cursor.position() > selection.m_cursor_begin.position()
&& cursor.position() <= selection.m_cursor_end.position())
return true;
}
return false;
}
......@@ -37,13 +37,14 @@ namespace TextEditor {
namespace Internal {
struct TEXTEDITOR_EXPORT OverlaySelection {
OverlaySelection():m_fixedLength(-1), m_dropShadow(false){}
OverlaySelection():m_fixedLength(-1), m_dropShadow(false), m_expandBegin(false){}
QTextCursor m_cursor_begin;
QTextCursor m_cursor_end;
QColor m_fg;
QColor m_bg;
int m_fixedLength;
bool m_dropShadow;
bool m_expandBegin;
};
class TEXTEDITOR_EXPORT TextEditorOverlay : public QObject
......@@ -78,13 +79,24 @@ public:
void setAlpha(bool enabled) { m_alpha = enabled; }
void clear();
void addOverlaySelection(const QTextCursor &cursor, const QColor &fg, const QColor &bg, bool lockSize = false);
void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg, bool lockSize = false, bool dropShadow = false);
enum OverlaySelectionFlags {
LockSize = 1,
DropShadow = 2,
ExpandBegin = 4
};
void addOverlaySelection(const QTextCursor &cursor, const QColor &fg, const QColor &bg,
uint overlaySelectionFlags = 0);
void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg,
uint overlaySelectionFlags = 0);
inline bool isEmpty() const { return m_selections.isEmpty(); }
inline int dropShadowWidth() const { return m_dropShadowWidth; }
bool hasCursorInSelection(const QTextCursor &cursor) const;
private:
QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip);
void paintSelection(QPainter *painter, const OverlaySelection &selection);
......
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