diff options
author | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-05-10 20:20:00 +0200 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@qt.io> | 2019-05-24 15:37:04 +0200 |
commit | 2c8fa9700cb1eb5f1587bec46b7060ec93c6b1d2 (patch) | |
tree | 9a7743c8818999c65193583d92ed10cd1fc3794d /src | |
parent | 12e0c7cbe0be22c288413080a1d1bd3beafb3008 (diff) |
Support clicking to toggle checkboxes in QTextEdit
Add QAbstractTextDocumentLayout::markerAt(pos) for hit testing.
(Qt Quick TextEdit needs it too.) Finds out whether the position
corresponds to a marker on a paragraph. I.e. it finds checkboxes in
GitHub-flavored markdown. This enables editor classes to toggle
checkboxes by clicking them.
Use it in QTextEdit to add the checkbox toggling feature. Also show the
"pointing finger" cursor when hovering a toggleable checkbox.
Change-Id: I036c967ab45e14c836272eac2cc7c7d652543c89
Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/text/qabstracttextdocumentlayout.cpp | 31 | ||||
-rw-r--r-- | src/gui/text/qabstracttextdocumentlayout.h | 1 | ||||
-rw-r--r-- | src/widgets/widgets/qtextedit.cpp | 17 | ||||
-rw-r--r-- | src/widgets/widgets/qtextedit.h | 1 | ||||
-rw-r--r-- | src/widgets/widgets/qtextedit_p.h | 3 | ||||
-rw-r--r-- | src/widgets/widgets/qwidgettextcontrol.cpp | 38 | ||||
-rw-r--r-- | src/widgets/widgets/qwidgettextcontrol_p.h | 3 | ||||
-rw-r--r-- | src/widgets/widgets/qwidgettextcontrol_p_p.h | 2 |
8 files changed, 96 insertions, 0 deletions
diff --git a/src/gui/text/qabstracttextdocumentlayout.cpp b/src/gui/text/qabstracttextdocumentlayout.cpp index 2278378613..5263ece87c 100644 --- a/src/gui/text/qabstracttextdocumentlayout.cpp +++ b/src/gui/text/qabstracttextdocumentlayout.cpp @@ -41,6 +41,7 @@ #include <qtextformat.h> #include "qtextdocument_p.h" #include "qtextengine_p.h" +#include "qtextlist.h" #include "qabstracttextdocumentlayout_p.h" @@ -650,6 +651,36 @@ QTextFormat QAbstractTextDocumentLayout::formatAt(const QPointF &pos) const } /*! + \since 5.14 + + Returns the block (probably a list item) whose \l{QTextBlockFormat::marker()}{marker} + is found at the given position \a pos. +*/ +QTextBlock QAbstractTextDocumentLayout::blockWithMarkerAt(const QPointF &pos) const +{ + QTextBlock block = document()->firstBlock(); + while (block.isValid()) { + if (block.blockFormat().marker() != QTextBlockFormat::NoMarker) { + QRectF blockBr = blockBoundingRect(block); + QTextBlockFormat blockFmt = block.blockFormat(); + QFontMetrics fm(block.charFormat().font()); + qreal totalIndent = blockFmt.indent() + blockFmt.leftMargin() + blockFmt.textIndent(); + if (block.textList()) + totalIndent += block.textList()->format().indent() * 40; + QRectF adjustedBr = blockBr.adjusted(totalIndent - fm.height(), 0, totalIndent - blockBr.width(), fm.height() - blockBr.height()); + if (adjustedBr.contains(pos)) { + //qDebug() << "hit block" << block.text() << blockBr << adjustedBr << "marker" << block.blockFormat().marker() + // << "font" << block.charFormat().font() << "adj" << lineHeight << totalIndent; + if (block.blockFormat().hasProperty(QTextFormat::BlockMarker)) + return block; + } + } + block = block.next(); + } + return QTextBlock(); +} + +/*! \fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const Returns the bounding rectangle of \a frame. diff --git a/src/gui/text/qabstracttextdocumentlayout.h b/src/gui/text/qabstracttextdocumentlayout.h index 3371401420..397dcd37d4 100644 --- a/src/gui/text/qabstracttextdocumentlayout.h +++ b/src/gui/text/qabstracttextdocumentlayout.h @@ -87,6 +87,7 @@ public: QString anchorAt(const QPointF& pos) const; QString imageAt(const QPointF &pos) const; QTextFormat formatAt(const QPointF &pos) const; + QTextBlock blockWithMarkerAt(const QPointF &pos) const; virtual int pageCount() const = 0; virtual QSizeF documentSize() const = 0; diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp index 5f734258b2..01f7c34f93 100644 --- a/src/widgets/widgets/qtextedit.cpp +++ b/src/widgets/widgets/qtextedit.cpp @@ -167,6 +167,7 @@ void QTextEditPrivate::init(const QString &html) QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool))); QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged())); + QObject::connect(control, SIGNAL(blockMarkerHovered(QTextBlock)), q, SLOT(_q_hoveredBlockWithMarkerChanged(QTextBlock))); QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus())); @@ -187,6 +188,7 @@ void QTextEditPrivate::init(const QString &html) vbar->setSingleStep(20); viewport->setBackgroundRole(QPalette::Base); + q->setMouseTracking(true); q->setAcceptDrops(true); q->setFocusPolicy(Qt::StrongFocus); q->setAttribute(Qt::WA_KeyCompression); @@ -228,6 +230,21 @@ void QTextEditPrivate::_q_cursorPositionChanged() #endif } +void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block) +{ + Q_Q(QTextEdit); + Qt::CursorShape cursor = cursorToRestoreAfterHover; + if (block.isValid() && !q->isReadOnly()) { + QTextBlockFormat::MarkerType marker = block.blockFormat().marker(); + if (marker != QTextBlockFormat::NoMarker) { + if (viewport->cursor().shape() != Qt::PointingHandCursor) + cursorToRestoreAfterHover = viewport->cursor().shape(); + cursor = Qt::PointingHandCursor; + } + } + viewport->setCursor(cursor); +} + void QTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode) { QTextCursor cursor = control->textCursor(); diff --git a/src/widgets/widgets/qtextedit.h b/src/widgets/widgets/qtextedit.h index 3b7e610786..09ef44b7b2 100644 --- a/src/widgets/widgets/qtextedit.h +++ b/src/widgets/widgets/qtextedit.h @@ -331,6 +331,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars()) Q_PRIVATE_SLOT(d_func(), void _q_ensureVisible(const QRectF &)) Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_hoveredBlockWithMarkerChanged(const QTextBlock &)) friend class QTextEditControl; friend class QTextDocument; friend class QWidgetTextControl; diff --git a/src/widgets/widgets/qtextedit_p.h b/src/widgets/widgets/qtextedit_p.h index c4ee75c78d..f7b4d15318 100644 --- a/src/widgets/widgets/qtextedit_p.h +++ b/src/widgets/widgets/qtextedit_p.h @@ -104,6 +104,7 @@ public: void _q_currentCharFormatChanged(const QTextCharFormat &format); void _q_cursorPositionChanged(); + void _q_hoveredBlockWithMarkerChanged(const QTextBlock &block); void updateDefaultTextOption(); @@ -136,6 +137,8 @@ public: QString placeholderText; + Qt::CursorShape cursorToRestoreAfterHover = Qt::IBeamCursor; + #ifdef QT_KEYPAD_NAVIGATION QBasicTimer deleteAllTimer; #endif diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index f85c7cdc6d..af3b03cd9e 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -1581,6 +1581,11 @@ void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton butto e->ignore(); return; } + bool wasValid = blockWithMarkerUnderMouse.isValid(); + blockWithMarkerUnderMouse = q->blockWithMarkerAt(pos); + if (wasValid != blockWithMarkerUnderMouse.isValid()) + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); + cursorIsFocusIndicator = false; const QTextCursor oldSelection = cursor; @@ -1599,6 +1604,8 @@ void QWidgetTextControlPrivate::mousePressEvent(QEvent *e, Qt::MouseButton butto selectedBlockOnTrippleClick = cursor; anchorOnMousePress = QString(); + blockWithMarkerUnderMouse = QTextBlock(); + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); trippleClickTimer.stop(); } else { @@ -1738,6 +1745,11 @@ void QWidgetTextControlPrivate::mouseMoveEvent(QEvent *e, Qt::MouseButton button } selectionChanged(true); repaintOldAndNewSelection(oldSelection); + } else { + bool wasValid = blockWithMarkerUnderMouse.isValid(); + blockWithMarkerUnderMouse = q->blockWithMarkerAt(mousePos); + if (wasValid != blockWithMarkerUnderMouse.isValid()) + emit q->blockMarkerHovered(blockWithMarkerUnderMouse); } sendMouseEventToInputContext(e, QEvent::MouseMove, button, mousePos, modifiers, buttons, globalPos); @@ -1787,6 +1799,26 @@ void QWidgetTextControlPrivate::mouseReleaseEvent(QEvent *e, Qt::MouseButton but emit q->microFocusChanged(); } + // toggle any checkbox that the user clicks + if ((interactionFlags & Qt::TextEditable) && (button & Qt::LeftButton) && + (blockWithMarkerUnderMouse.isValid()) && !cursor.hasSelection()) { + QTextBlock markerBlock = q->blockWithMarkerAt(pos); + if (markerBlock == blockWithMarkerUnderMouse) { + auto fmt = blockWithMarkerUnderMouse.blockFormat(); + switch (fmt.marker()) { + case QTextBlockFormat::Unchecked : + fmt.setMarker(QTextBlockFormat::Checked); + break; + case QTextBlockFormat::Checked: + fmt.setMarker(QTextBlockFormat::Unchecked); + break; + default: + break; + } + cursor.setBlockFormat(fmt); + } + } + if (interactionFlags & Qt::LinksAccessibleByMouse) { if (!(button & Qt::LeftButton)) return; @@ -2385,6 +2417,12 @@ QString QWidgetTextControl::anchorAtCursor() const return d->anchorForCursor(d->cursor); } +QTextBlock QWidgetTextControl::blockWithMarkerAt(const QPointF &pos) const +{ + Q_D(const QWidgetTextControl); + return d->doc->documentLayout()->blockWithMarkerAt(pos); +} + bool QWidgetTextControl::overwriteMode() const { Q_D(const QWidgetTextControl); diff --git a/src/widgets/widgets/qwidgettextcontrol_p.h b/src/widgets/widgets/qwidgettextcontrol_p.h index e521e7b356..59bf5466e6 100644 --- a/src/widgets/widgets/qwidgettextcontrol_p.h +++ b/src/widgets/widgets/qwidgettextcontrol_p.h @@ -150,6 +150,8 @@ public: QString anchorAtCursor() const; + QTextBlock blockWithMarkerAt(const QPointF &pos) const; + bool overwriteMode() const; void setOverwriteMode(bool overwrite); @@ -242,6 +244,7 @@ Q_SIGNALS: void microFocusChanged(); void linkActivated(const QString &link); void linkHovered(const QString &); + void blockMarkerHovered(const QTextBlock &block); void modificationChanged(bool m); public: diff --git a/src/widgets/widgets/qwidgettextcontrol_p_p.h b/src/widgets/widgets/qwidgettextcontrol_p_p.h index 6ccdfafe2b..c77a31bedf 100644 --- a/src/widgets/widgets/qwidgettextcontrol_p_p.h +++ b/src/widgets/widgets/qwidgettextcontrol_p_p.h @@ -55,6 +55,7 @@ #include "QtGui/qtextdocumentfragment.h" #include "QtGui/qtextcursor.h" #include "QtGui/qtextformat.h" +#include "QtGui/qtextobject.h" #if QT_CONFIG(menu) #include "QtWidgets/qmenu.h" #endif @@ -227,6 +228,7 @@ public: QString highlightedAnchor; // Anchor below cursor QString anchorOnMousePress; + QTextBlock blockWithMarkerUnderMouse; bool hadSelectionOnMousePress; bool ignoreUnusedNavigationEvents; |