From 4a5259929245dd204dee6061c614b369441f6724 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Thu, 25 Apr 2019 16:48:39 +0300 Subject: QQuickTextControl: Emit cursorPositionChanged() when handling IM event QQuickTextControl and consequently QQuickTextEdit did not emit cursorPositionChanged() signal when cursor position was changed by a QInputMethodEvent with a QInputMethodEvent::Selection attribute. This is especially important on Android because QAndroidInputContext uses such events extensively to move the cursor and also relies on cursorPositionChanged() signal being emitted by the focus object. If the signal is not emitted, QAndroidInputContext does not notify the virtual keyboard about cursor position change which results in various glitches. Change-Id: I46bef6185463d11507b1b96435fdc056bbe951f2 Reviewed-by: Shawn Rutledge Reviewed-by: Richard Moe Gustavsen --- src/quick/items/qquicktextcontrol.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 38ca7283b4..1dd593cc1a 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -1297,6 +1297,7 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) || e->preeditString() != cursor.block().layout()->preeditAreaText() || e->replacementLength() > 0; bool forceSelectionChanged = false; + int oldCursorPos = cursor.position(); cursor.beginEditBlock(); if (isGettingInput) { @@ -1365,6 +1366,8 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QTextCursorPrivate *cursor_d = QTextCursorPrivate::getPrivate(&cursor); if (cursor_d) cursor_d->setX(); + if (cursor.position() != oldCursorPos) + emit q->cursorPositionChanged(); q->updateCursorRectangle(oldPreeditCursor != preeditCursor || forceSelectionChanged || isGettingInput); selectionChanged(forceSelectionChanged); } -- cgit v1.2.3 From 1db6782d6081fa54e7c951c21ac7c8953d2c9bfb Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 1 Apr 2019 13:10:35 +0200 Subject: QQuickTableView: optimize forceLayout() to start updating from the root Change-Id: Ib2f195780415836ebb03c151a6586fd7b0fb77b8 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 8f5130fc17..9e795f3c8c 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -770,13 +770,12 @@ void QQuickTableViewPrivate::forceLayout() scheduleRebuildTable(rebuildOptions); - if (polishing) { + auto rootView = rootSyncView(); + const bool updated = rootView->d_func()->updateTableRecursive(); + if (!updated) { qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!"; - q_func()->polish(); - return; + rootView->polish(); } - - updatePolish(); } void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest() -- cgit v1.2.3 From 55c497ecf47894c602c1b4a08099ac63546b9260 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 2 Apr 2019 09:14:17 +0200 Subject: QQuickTableView: protect fixup() from being called while resizing the content view Calling the base class implementation of fixup might move the content view and start animations etc, which will cause glitches. So ensure we don't do this when we adjust the content size internally. Change-Id: I214a6ae2da0c21fd733ea884bccb5e77fc554615 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 17 ++++++++++++++++- src/quick/items/qquicktableview_p_p.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 9e795f3c8c..ae1daccb39 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -616,6 +616,7 @@ void QQuickTableViewPrivate::updateContentWidth() Q_Q(QQuickTableView); if (syncHorizontally) { + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentWidth(syncView->contentWidth()); return; } @@ -632,6 +633,8 @@ void QQuickTableViewPrivate::updateContentWidth() const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; const qreal estimatedWidth = loadedTableOuterRect.right() + estimatedRemainingWidth; + + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentWidth(estimatedWidth); } @@ -640,6 +643,7 @@ void QQuickTableViewPrivate::updateContentHeight() Q_Q(QQuickTableView); if (syncVertically) { + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentHeight(syncView->contentHeight()); return; } @@ -656,6 +660,8 @@ void QQuickTableViewPrivate::updateContentHeight() const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing; const qreal estimatedHeight = loadedTableOuterRect.bottom() + estimatedRemainingHeight; + + QBoolBlocker fixupGuard(inUpdateContentSize, true); q->QQuickFlickable::setContentHeight(estimatedHeight); } @@ -1924,8 +1930,17 @@ bool QQuickTableViewPrivate::updateTable() void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent) { - if (scheduledRebuildOptions || rebuildState != RebuildState::Done) + if (inUpdateContentSize) { + // We update the content size dynamically as we load and unload edges. + // Unfortunately, this also triggers a call to this function. The base + // implementation will do things like start a momentum animation or move + // the content view somewhere else, which causes glitches. This can + // especially happen if flicking on one of the syncView children, which triggers + // an update to our content size. In that case, the base implementation don't know + // that the view is being indirectly dragged, and will therefore do strange things as + // it tries to 'fixup' the geometry. So we use a guard to prevent this from happening. return; + } QQuickFlickablePrivate::fixup(data, minExtent, maxExtent); } diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 7f2aee9105..e2a413db2c 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -272,6 +272,7 @@ public: bool syncHorizontally = false; bool inSetLocalViewportPos = false; bool inSyncViewportPosRecursive = false; + bool inUpdateContentSize = false; QJSValue rowHeightProvider; QJSValue columnWidthProvider; -- cgit v1.2.3 From 6bce577535d05635a8adef1355ffeddeb5d3c65a Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 9 Apr 2019 14:55:27 +0200 Subject: QQuickTableView: check if we need to rebuild when syncing with syncView Since different tables can have different sized models, it can also happen, as the views are being flicked around, that some views temporarily end up with no visible rows and columns in the viewport. When that happens, we continually check if the columns should become visible again, and if so, schedule a rebuild. Change-Id: Ic84e47fd5d7968c1f1408eb122e38fa841e7aec7 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index ae1daccb39..e169ac4aec 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -2025,13 +2025,11 @@ void QQuickTableViewPrivate::syncWithPendingChanges() Q_Q(QQuickTableView); viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height()); - // Sync rebuild options first, in case we schedule a rebuild from one of the - // other sync calls above. If so, we need to start a new rebuild from the top. - syncRebuildOptions(); - syncModel(); syncDelegate(); syncSyncView(); + + syncRebuildOptions(); } void QQuickTableViewPrivate::syncRebuildOptions() @@ -2121,7 +2119,6 @@ void QQuickTableViewPrivate::syncSyncView() assignedSyncView->d_func()->syncChildren.append(q); scheduledRebuildOptions |= RebuildOption::ViewportOnly; - q->polish(); } syncView = assignedSyncView; @@ -2134,6 +2131,21 @@ void QQuickTableViewPrivate::syncSyncView() q->setColumnSpacing(syncView->columnSpacing()); if (syncVertically) q->setRowSpacing(syncView->rowSpacing()); + + if (syncView && loadedItems.isEmpty() && !tableSize.isEmpty()) { + // When we have a syncView, we can sometimes temporarily end up with no loaded items. + // This can happen if the syncView has a model with more rows or columns than us, in + // which case the viewport can end up in a place where we have no rows or columns to + // show. In that case, check now if the viewport has been flicked back again, and + // that we can rebuild the table with a visible top-left cell. + const auto syncView_d = syncView->d_func(); + if (!syncView_d->loadedItems.isEmpty()) { + if (syncHorizontally && syncView_d->leftColumn() <= tableSize.width() - 1) + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly; + else if (syncVertically && syncView_d->topRow() <= tableSize.height() - 1) + scheduledRebuildOptions |= QQuickTableViewPrivate::RebuildOption::ViewportOnly; + } + } } void QQuickTableViewPrivate::connectToModel() -- cgit v1.2.3 From 624aff9125951229c98880afd5003fcb474d1d69 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 3 May 2019 08:51:09 +0200 Subject: Improve Context2D's line dash-related documentation Fix spelling errors, punctuation, and links. Change-Id: I79154d0da6acf403903f102655d5dcb64240b137 Reviewed-by: Shawn Rutledge --- src/quick/items/context2d/qquickcontext2d.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 57e697bdd1..1c7f3ecc6f 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -2001,7 +2001,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionO \since QtQuick 2.11 Returns an array of qreals representing the dash pattern of the line. - \sa setLineDash() + \sa setLineDash(), lineDashOffset */ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { @@ -2023,7 +2023,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::Fun /*! \qmlmethod QtQuick::Context2D::setLineDash(array pattern) \since QtQuick 2.11 - Sets the dash pattern to the given pattern + Sets the dash pattern to the given pattern. \a pattern a list of numbers that specifies distances to alternately draw a line and a gap. @@ -2042,7 +2042,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::Fun \endcode \endtable - \sa setLineDash + \sa getLineDash(), lineDashOffset */ QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) { @@ -2084,8 +2084,10 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::Fun \qmlproperty real QtQuick::Context2D::lineDashOffset \since QtQuick 2.11 - Holds the current line dash offset - The default line dash ofset value is 0 + Holds the current line dash offset. + The default line dash offset value is \c 0. + + \sa getLineDash(), setLineDash() */ QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { -- cgit v1.2.3 From 4a4842118d2303a8d851d1d8b85fe182d3fe492a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 6 May 2019 16:03:37 +0200 Subject: Accessibility: Make sure StaticText is marked read-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test more of the text properties. This is still very incomplete, but a small step forward. Also make sure that editable text reports the editable state. Fixes: QTBUG-75002 Change-Id: I9e43c980d8fa91671acb4e40e5d9162854884ee7 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickaccessibleattached_p.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 215a1e5db6..e292c280df 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -128,13 +128,19 @@ public: case QAccessible::Button: case QAccessible::MenuItem: case QAccessible::PageTab: - case QAccessible::EditableText: case QAccessible::SpinBox: case QAccessible::ComboBox: case QAccessible::Terminal: case QAccessible::ScrollBar: m_state.focusable = true; break; + case QAccessible::EditableText: + m_state.editable = true; + m_state.focusable = true; + break; + case QAccessible::StaticText: + m_state.readOnly = true; + break; default: break; } -- cgit v1.2.3 From 7aa25ad44606c26303e3bd3252a641552565cf4b Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 30 Apr 2019 13:04:15 +0200 Subject: QQuickTableView: remove relayoutTable() It was only called from one place. And we can optimize it a bit by moving the contents to the caller. Besides, stray relayouts without rebuilding (RebuildOption::LayoutOnly) is no longer allowed. Change-Id: Id63bd2d71969b81ea999caa9d4d331abf8999704 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 17 +++-------------- src/quick/items/qquicktableview_p_p.h | 1 - 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index e169ac4aec..17352221d3 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1217,19 +1217,6 @@ bool QQuickTableViewPrivate::isRowHidden(int row) return qFuzzyIsNull(getRowHeight(row)); } -void QQuickTableViewPrivate::relayoutTable() -{ - clearEdgeSizeCache(); - relayoutTableItems(); - syncLoadedTableRectFromLoadedTable(); - enforceTableAtOrigin(); - updateContentWidth(); - updateContentHeight(); - // Return back to updatePolish to loadAndUnloadVisibleEdges() - // since the re-layout might have caused some edges to be pushed - // out, while others might have been pushed in. -} - void QQuickTableViewPrivate::relayoutTableItems() { qCDebug(lcTableViewDelegateLifecycle); @@ -1695,7 +1682,9 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() // columns, since during the process, we didn't have all the items // available yet for the calculation. So we do it now. The exception // is if we specifically only requested a relayout. - relayoutTable(); + clearEdgeSizeCache(); + relayoutTableItems(); + syncLoadedTableRectFromLoadedTable(); } updateAverageEdgeSize(); diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index e2a413db2c..748a1478ec 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -338,7 +338,6 @@ public: bool updateTableRecursive(); bool updateTable(); - void relayoutTable(); void relayoutTableItems(); void layoutVerticalEdge(Qt::Edge tableEdge); -- cgit v1.2.3 From 96b99fb2acc5fe3704f2a472a9a71009b616e69e Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 3 May 2019 13:37:41 +0200 Subject: QQuickTableView: improve updateAverageEdgeSize() When we know the exact size of the content view, we can take advantage of this to calculate the exact average cell size. This in turn will improve the positioning of rows and columns whenever we need to rebuild, since we have a better idea where they should end up in the content view. Change-Id: I46c3e87eb38ab032df7c28b6144d1b2de1b9d4ef Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 17352221d3..48736f8d47 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -714,12 +714,21 @@ void QQuickTableViewPrivate::enforceTableAtOrigin() void QQuickTableViewPrivate::updateAverageEdgeSize() { - const int loadedRowCount = loadedRows.count(); - const int loadedColumnCount = loadedColumns.count(); - const qreal accRowSpacing = (loadedRowCount - 1) * cellSpacing.height(); - const qreal accColumnSpacing = (loadedColumnCount - 1) * cellSpacing.width(); - averageEdgeSize.setHeight((loadedTableOuterRect.height() - accRowSpacing) / loadedRowCount); - averageEdgeSize.setWidth((loadedTableOuterRect.width() - accColumnSpacing) / loadedColumnCount); + if (explicitContentWidth.isValid()) { + const qreal accColumnSpacing = (tableSize.width() - 1) * cellSpacing.width(); + averageEdgeSize.setWidth((explicitContentWidth - accColumnSpacing) / tableSize.width()); + } else { + const qreal accColumnSpacing = (loadedColumns.count() - 1) * cellSpacing.width(); + averageEdgeSize.setWidth((loadedTableOuterRect.width() - accColumnSpacing) / loadedColumns.count()); + } + + if (explicitContentHeight.isValid()) { + const qreal accRowSpacing = (tableSize.height() - 1) * cellSpacing.height(); + averageEdgeSize.setHeight((explicitContentHeight - accRowSpacing) / tableSize.height()); + } else { + const qreal accRowSpacing = (loadedRows.count() - 1) * cellSpacing.height(); + averageEdgeSize.setHeight((loadedTableOuterRect.height() - accRowSpacing) / loadedRows.count()); + } } void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() -- cgit v1.2.3 From fd6321c03e2d63997078bfa41332dbddefbb86b0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 5 Apr 2019 09:59:10 +0200 Subject: Remove last traces of QV8Engine Change-Id: I59f738402d51e39188bbbca2ef1fbc8a61612372 Reviewed-by: Simon Hausmann --- src/quick/items/context2d/qquickcontext2d.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/quick/items') diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 58bc12a221..5640721c6f 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -473,7 +473,7 @@ static QFont qt_font_from_string(const QString& fontString, const QFont ¤t return newFont; } -class QQuickContext2DEngineData : public QV8Engine::Deletable +class QQuickContext2DEngineData : public QV4::ExecutionEngine::Deletable { public: QQuickContext2DEngineData(QV4::ExecutionEngine *engine); -- cgit v1.2.3 From d12cc4f6b58ecb5f44458002fe9b74d0c84f10ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 7 May 2019 14:58:29 +0200 Subject: Don't overwrite states if role is assigned after a state We therefore need to keep track of which states have been explicitly set or not in order to know which ones should get initialized with their defaults. Change-Id: I49fdae82288f04ea4f50d45735a93434ac02abec Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickaccessibleattached.cpp | 43 ++++++++++++++++++++++++++++ src/quick/items/qquickaccessibleattached_p.h | 38 ++---------------------- 2 files changed, 46 insertions(+), 35 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 0168c3160c..c150e4efa2 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -390,6 +390,49 @@ QQuickAccessibleAttached::~QQuickAccessibleAttached() { } +void QQuickAccessibleAttached::setRole(QAccessible::Role role) +{ + if (role != m_role) { + m_role = role; + Q_EMIT roleChanged(); + // There is no way to signify role changes at the moment. + // QAccessible::updateAccessibility(parent(), 0, QAccessible::); + + switch (role) { + case QAccessible::CheckBox: + case QAccessible::RadioButton: + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; + if (!m_stateExplicitlySet.checkable) + m_state.checkable = true; + break; + case QAccessible::Button: + case QAccessible::MenuItem: + case QAccessible::PageTab: + case QAccessible::SpinBox: + case QAccessible::ComboBox: + case QAccessible::Terminal: + case QAccessible::ScrollBar: + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; + break; + case QAccessible::EditableText: + if (!m_stateExplicitlySet.editable) + m_state.editable = true; + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; + break; + case QAccessible::StaticText: + if (!m_stateExplicitlySet.readOnly) { + m_state.readOnly = true; + } + break; + default: + break; + } + } +} + QQuickAccessibleAttached *QQuickAccessibleAttached::qmlAttachedProperties(QObject *obj) { return new QQuickAccessibleAttached(obj); diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index e292c280df..f4194ef13d 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -69,6 +69,7 @@ QT_BEGIN_NAMESPACE bool P() const { return m_state.P ; } \ void set_ ## P(bool arg) \ { \ + m_stateExplicitlySet.P = true; \ if (m_state.P == arg) \ return; \ m_state.P = arg; \ @@ -111,41 +112,7 @@ public: ~QQuickAccessibleAttached(); QAccessible::Role role() const { return m_role; } - void setRole(QAccessible::Role role) - { - if (role != m_role) { - m_role = role; - Q_EMIT roleChanged(); - // There is no way to signify role changes at the moment. - // QAccessible::updateAccessibility(parent(), 0, QAccessible::); - - switch (role) { - case QAccessible::CheckBox: - case QAccessible::RadioButton: - m_state.focusable = true; - m_state.checkable = true; - break; - case QAccessible::Button: - case QAccessible::MenuItem: - case QAccessible::PageTab: - case QAccessible::SpinBox: - case QAccessible::ComboBox: - case QAccessible::Terminal: - case QAccessible::ScrollBar: - m_state.focusable = true; - break; - case QAccessible::EditableText: - m_state.editable = true; - m_state.focusable = true; - break; - case QAccessible::StaticText: - m_state.readOnly = true; - break; - default: - break; - } - } - } + void setRole(QAccessible::Role role); QString name() const { if (m_state.passwordEdit) return QString(); @@ -247,6 +214,7 @@ private: QAccessible::Role m_role; QAccessible::State m_state; + QAccessible::State m_stateExplicitlySet; QString m_name; QString m_description; -- cgit v1.2.3 From dafdc9d701b0b3a33e3eac0a723d80717a02f4f8 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 6 May 2019 19:42:49 +0200 Subject: QQuickTableView: sync viewport rect with syncView when rebuilding When moving contentX/Y, we also need to ensure that the viewport rect reflects the change. Otherwise we'll end up loading rows and columns somewhere else then under the viewport. Change-Id: Ifbd3d66b9b3a822414aefde9b5bd088274dfa2ad Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 48736f8d47..af4373317b 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1644,10 +1644,15 @@ void QQuickTableViewPrivate::beginRebuildTable() loadedTableInnerRect = QRect(); clearEdgeSizeCache(); - if (syncHorizontally) + if (syncHorizontally) { setLocalViewportX(syncView->contentX()); - if (syncVertically) + viewportRect.moveLeft(syncView->d_func()->viewportRect.left()); + } + + if (syncVertically) { setLocalViewportY(syncView->contentY()); + viewportRect.moveTop(syncView->d_func()->viewportRect.top()); + } if (!model) { qCDebug(lcTableViewDelegateLifecycle()) << "no model found, leaving table empty"; -- cgit v1.2.3 From 0eb10519c263187f54ffad5c6d521d88736f2680 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 21 Dec 2018 23:38:46 +0100 Subject: Add Markdown support to Text "Markers" (only checkboxes so far) are a new feature too. Change-Id: I5a0ea337ec6cf8bdadf3b4729caaa579e8b625b5 Reviewed-by: Gatis Paeglis --- src/quick/items/qquicktext.cpp | 16 ++++++++++++---- src/quick/items/qquicktext_p.h | 1 + src/quick/items/qquicktext_p_p.h | 1 + src/quick/items/qquicktextdocument.cpp | 8 ++++++++ src/quick/items/qquicktextdocument_p.h | 3 +++ src/quick/items/qquicktextnodeengine.cpp | 11 +++++++++++ 6 files changed, 36 insertions(+), 4 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 6d343a91ce..ae849aeb4b 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1653,13 +1653,17 @@ void QQuickText::setText(const QString &n) if (d->text == n) return; - d->richText = d->format == RichText; + d->markdownText = d->format == MarkdownText; + d->richText = d->format == RichText || d->markdownText; d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n)); d->text = n; if (isComponentComplete()) { if (d->richText) { d->ensureDoc(); - d->extra->doc->setText(n); + if (d->markdownText) + d->extra->doc->setMarkdownText(n); + else + d->extra->doc->setText(n); d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft(); } else { d->clearFormats(); @@ -2146,7 +2150,8 @@ void QQuickText::setTextFormat(TextFormat format) return; d->format = format; bool wasRich = d->richText; - d->richText = format == RichText; + d->markdownText = format == MarkdownText; + d->richText = format == RichText || d->markdownText; d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text)); if (isComponentComplete()) { @@ -2687,7 +2692,10 @@ void QQuickText::componentComplete() if (d->updateOnComponentComplete) { if (d->richText) { d->ensureDoc(); - d->extra->doc->setText(d->text); + if (d->markdownText) + d->extra->doc->setMarkdownText(d->text); + else + d->extra->doc->setText(d->text); d->rightToLeftText = d->extra->doc->toPlainText().isRightToLeft(); } else { d->rightToLeftText = d->text.isRightToLeft(); diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index 1af60051fb..45f387cb12 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -121,6 +121,7 @@ public: Q_ENUM(TextStyle) enum TextFormat { PlainText = Qt::PlainText, RichText = Qt::RichText, + MarkdownText = Qt::MarkdownText, AutoText = Qt::AutoText, StyledText = 4 }; Q_ENUM(TextFormat) diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index efa45e0958..c01998b100 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -161,6 +161,7 @@ public: bool updateOnComponentComplete:1; bool richText:1; bool styledText:1; + bool markdownText:1; bool widthExceeded:1; bool heightExceeded:1; bool internalWidthUpdate:1; diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp index 06ac5804c4..021bbca0f6 100644 --- a/src/quick/items/qquicktextdocument.cpp +++ b/src/quick/items/qquicktextdocument.cpp @@ -237,6 +237,14 @@ void QQuickTextDocumentWithImageResources::setText(const QString &text) #endif } +#if QT_CONFIG(textmarkdownreader) +void QQuickTextDocumentWithImageResources::setMarkdownText(const QString &text) +{ + clearResources(); + QTextDocument::setMarkdown(text); +} +#endif + QSet QQuickTextDocumentWithImageResources::errors; QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextdocument_p.h b/src/quick/items/qquicktextdocument_p.h index 1218b12b89..9c5152d442 100644 --- a/src/quick/items/qquicktextdocument_p.h +++ b/src/quick/items/qquicktextdocument_p.h @@ -73,6 +73,9 @@ public: virtual ~QQuickTextDocumentWithImageResources(); void setText(const QString &); +#if QT_CONFIG(textmarkdownreader) + void setMarkdownText(const QString &); +#endif int resourcesLoading() const { return outstanding; } QSizeF intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) override; diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index a1b5eb1faf..32478ba375 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -1011,6 +1011,17 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText break; }; + switch (block.blockFormat().marker()) { + case QTextBlockFormat::Checked: + listItemBullet = QChar(0x2612); // Checked checkbox + break; + case QTextBlockFormat::Unchecked: + listItemBullet = QChar(0x2610); // Unchecked checkbox + break; + case QTextBlockFormat::NoMarker: + break; + } + QSizeF size(fontMetrics.horizontalAdvance(listItemBullet), fontMetrics.height()); qreal xoff = fontMetrics.horizontalAdvance(QLatin1Char(' ')); if (block.textDirection() == Qt::LeftToRight) -- cgit v1.2.3 From 3fff03048a48aa7ead6e888165b2ae5a118d675c Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Thu, 2 May 2019 10:26:48 +0200 Subject: QQuickTableView: change implementation of enforceTableAtOrigin() We set the size of the content view to be the size of the complete table. The problem is that the exact size will always be just a prediction, since we would otherwise need to iterate over all rows and column up front, to be able calculate the exact size. This is not acceptable when using non-trival table models. A side effect of this, is that is will be possible to flick the viewport further out than the actual end of the table, if the content view turns out to be larger than the table itself. From before we used to just move the whole table back into the viewport when that happened, which could be seen as a sudden jump of the table to a new position. This change will improve this logic so that we can avoid most visual jumps. Instead of moving the table around, QQuickFlickable supports moving the origin instead. So when we see that the table is not in sync with the content view, we simple move the origin to the edge of the table. The effect is that any flicking or ongoing momentum animation in QQuickFlickable will continue as if nothing happened. This is also the same logic used by QQuickListView. Change-Id: I6060b7e84b9489c8fa569e6ff41b958e3871f8e7 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 233 ++++++++++++++++++++++++++++------ src/quick/items/qquicktableview_p.h | 5 + src/quick/items/qquicktableview_p_p.h | 5 +- 3 files changed, 204 insertions(+), 39 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index af4373317b..2b49200eaa 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -665,50 +665,179 @@ void QQuickTableViewPrivate::updateContentHeight() q->QQuickFlickable::setContentHeight(estimatedHeight); } -void QQuickTableViewPrivate::enforceTableAtOrigin() -{ - // Gaps before the first row/column can happen if rows/columns - // changes size while flicking e.g because of spacing changes or - // changes to a column maxWidth/row maxHeight. Check for this, and - // move the whole table rect accordingly. - bool layoutNeeded = false; - const qreal flickMargin = 50; - - const bool noMoreColumns = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge) == kEdgeIndexAtEnd; - const bool noMoreRows = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge) == kEdgeIndexAtEnd; - - if (noMoreColumns) { - if (!qFuzzyIsNull(loadedTableOuterRect.left())) { - // There are no more columns, but the table rect - // is not at origin. So we move it there. - loadedTableOuterRect.moveLeft(0); - layoutNeeded = true; +void QQuickTableViewPrivate::updateExtents() +{ + // When rows or columns outside the viewport are removed or added, or a rebuild + // forces us to guesstimate a new top-left, the edges of the table might end up + // out of sync with the edges of the content view. We detect this situation here, and + // move the origin to ensure that there will never be gaps at the end of the table. + // Normally we detect that the size of the whole table is not going to be equal to the + // size of the content view already when we load the last row/column, and especially + // before it's flicked completely inside the viewport. For those cases we simply adjust + // the origin/endExtent, to give a smooth flicking experience. + // But if flicking fast (e.g with a scrollbar), it can happen that the viewport ends up + // outside the end of the table in just one viewport update. To avoid a "blink" in the + // viewport when that happens, we "move" the loaded table into the viewport to cover it. + Q_Q(QQuickTableView); + + bool tableMovedHorizontally = false; + bool tableMovedVertically = false; + + const int nextLeftColumn = nextVisibleEdgeIndexAroundLoadedTable(Qt::LeftEdge); + const int nextRightColumn = nextVisibleEdgeIndexAroundLoadedTable(Qt::RightEdge); + const int nextTopRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::TopEdge); + const int nextBottomRow = nextVisibleEdgeIndexAroundLoadedTable(Qt::BottomEdge); + + if (syncHorizontally) { + const auto syncView_d = syncView->d_func(); + origin.rx() = syncView_d->origin.x(); + endExtent.rwidth() = syncView_d->endExtent.width(); + hData.markExtentsDirty(); + } else if (nextLeftColumn == kEdgeIndexAtEnd) { + // There are no more columns to load on the left side of the table. + // In that case, we ensure that the origin match the beginning of the table. + if (loadedTableOuterRect.left() > viewportRect.left()) { + // We have a blank area at the left end of the viewport. In that case we don't have time to + // wait for the viewport to move (after changing origin), since that will take an extra + // update cycle, which will be visible as a blink. Instead, unless the blank spot is just + // us overshooting, we brute force the loaded table inside the already existing viewport. + if (loadedTableOuterRect.left() > origin.x()) { + const qreal diff = loadedTableOuterRect.left() - origin.x(); + loadedTableOuterRect.moveLeft(loadedTableOuterRect.left() - diff); + loadedTableInnerRect.moveLeft(loadedTableInnerRect.left() - diff); + tableMovedHorizontally = true; + } } - } else { - if (loadedTableOuterRect.left() <= 0) { - // The table rect is at origin, or outside. But we still have - // more visible columns to the left. So we need to make some - // space so that they can be flicked in. - loadedTableOuterRect.moveLeft(flickMargin); - layoutNeeded = true; + origin.rx() = loadedTableOuterRect.left(); + hData.markExtentsDirty(); + } else if (loadedTableOuterRect.left() <= origin.x() + cellSpacing.width()) { + // The table rect is at the origin, or outside, but we still have more + // visible columns to the left. So we try to guesstimate how much space + // the rest of the columns will occupy, and move the origin accordingly. + const int columnsRemaining = nextLeftColumn + 1; + const qreal remainingColumnWidths = columnsRemaining * averageEdgeSize.width(); + const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); + const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; + origin.rx() = loadedTableOuterRect.left() - estimatedRemainingWidth; + hData.markExtentsDirty(); + } else if (nextRightColumn == kEdgeIndexAtEnd) { + // There are no more columns to load on the right side of the table. + // In that case, we ensure that the end of the content view match the end of the table. + if (loadedTableOuterRect.right() < viewportRect.right()) { + // We have a blank area at the right end of the viewport. In that case we don't have time to + // wait for the viewport to move (after changing endExtent), since that will take an extra + // update cycle, which will be visible as a blink. Instead, unless the blank spot is just + // us overshooting, we brute force the loaded table inside the already existing viewport. + const qreal w = qMin(viewportRect.right(), q->contentWidth() + endExtent.width()); + if (loadedTableOuterRect.right() < w) { + const qreal diff = loadedTableOuterRect.right() - w; + loadedTableOuterRect.moveRight(loadedTableOuterRect.right() - diff); + loadedTableInnerRect.moveRight(loadedTableInnerRect.right() - diff); + tableMovedHorizontally = true; + } } + endExtent.rwidth() = loadedTableOuterRect.right() - q->contentWidth(); + hData.markExtentsDirty(); + } else if (loadedTableOuterRect.right() >= q->contentWidth() + endExtent.width() - cellSpacing.width()) { + // The right-most column is outside the end of the content view, and we + // still have more visible columns in the model. This can happen if the application + // has set a fixed content width. + const int columnsRemaining = tableSize.width() - nextRightColumn; + const qreal remainingColumnWidths = columnsRemaining * averageEdgeSize.width(); + const qreal remainingSpacing = columnsRemaining * cellSpacing.width(); + const qreal estimatedRemainingWidth = remainingColumnWidths + remainingSpacing; + const qreal pixelsOutsideContentWidth = loadedTableOuterRect.right() - q->contentWidth(); + endExtent.rwidth() = pixelsOutsideContentWidth + estimatedRemainingWidth; + hData.markExtentsDirty(); } - if (noMoreRows) { - if (!qFuzzyIsNull(loadedTableOuterRect.top())) { - loadedTableOuterRect.moveTop(0); - layoutNeeded = true; + if (syncVertically) { + const auto syncView_d = syncView->d_func(); + origin.ry() = syncView_d->origin.y(); + endExtent.rheight() = syncView_d->endExtent.height(); + vData.markExtentsDirty(); + } else if (nextTopRow == kEdgeIndexAtEnd) { + // There are no more rows to load on the top side of the table. + // In that case, we ensure that the origin match the beginning of the table. + if (loadedTableOuterRect.top() > viewportRect.top()) { + // We have a blank area at the top of the viewport. In that case we don't have time to + // wait for the viewport to move (after changing origin), since that will take an extra + // update cycle, which will be visible as a blink. Instead, unless the blank spot is just + // us overshooting, we brute force the loaded table inside the already existing viewport. + if (loadedTableOuterRect.top() > origin.y()) { + const qreal diff = loadedTableOuterRect.top() - origin.y(); + loadedTableOuterRect.moveTop(loadedTableOuterRect.top() - diff); + loadedTableInnerRect.moveTop(loadedTableInnerRect.top() - diff); + tableMovedVertically = true; + } } - } else { - if (loadedTableOuterRect.top() <= 0) { - loadedTableOuterRect.moveTop(flickMargin); - layoutNeeded = true; + origin.ry() = loadedTableOuterRect.top(); + vData.markExtentsDirty(); + } else if (loadedTableOuterRect.top() <= origin.y() + cellSpacing.height()) { + // The table rect is at the origin, or outside, but we still have more + // visible rows at the top. So we try to guesstimate how much space + // the rest of the rows will occupy, and move the origin accordingly. + const int rowsRemaining = nextTopRow + 1; + const qreal remainingRowHeights = rowsRemaining * averageEdgeSize.height(); + const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); + const qreal estimatedRemainingHeight = remainingRowHeights + remainingSpacing; + origin.ry() = loadedTableOuterRect.top() - estimatedRemainingHeight; + vData.markExtentsDirty(); + } else if (nextBottomRow == kEdgeIndexAtEnd) { + // There are no more rows to load on the bottom side of the table. + // In that case, we ensure that the end of the content view match the end of the table. + if (loadedTableOuterRect.bottom() < viewportRect.bottom()) { + // We have a blank area at the bottom of the viewport. In that case we don't have time to + // wait for the viewport to move (after changing endExtent), since that will take an extra + // update cycle, which will be visible as a blink. Instead, unless the blank spot is just + // us overshooting, we brute force the loaded table inside the already existing viewport. + const qreal h = qMin(viewportRect.bottom(), q->contentHeight() + endExtent.height()); + if (loadedTableOuterRect.bottom() < h) { + const qreal diff = loadedTableOuterRect.bottom() - h; + loadedTableOuterRect.moveBottom(loadedTableOuterRect.bottom() - diff); + loadedTableInnerRect.moveBottom(loadedTableInnerRect.bottom() - diff); + tableMovedVertically = true; + } + } + endExtent.rheight() = loadedTableOuterRect.bottom() - q->contentHeight(); + vData.markExtentsDirty(); + } else if (loadedTableOuterRect.bottom() >= q->contentHeight() + endExtent.height() - cellSpacing.height()) { + // The bottom-most row is outside the end of the content view, and we + // still have more visible rows in the model. This can happen if the application + // has set a fixed content height. + const int rowsRemaining = tableSize.height() - nextBottomRow; + const qreal remainingRowHeigts = rowsRemaining * averageEdgeSize.height(); + const qreal remainingSpacing = rowsRemaining * cellSpacing.height(); + const qreal estimatedRemainingHeight = remainingRowHeigts + remainingSpacing; + const qreal pixelsOutsideContentHeight = loadedTableOuterRect.bottom() - q->contentHeight(); + endExtent.rheight() = pixelsOutsideContentHeight + estimatedRemainingHeight; + vData.markExtentsDirty(); + } + + if (tableMovedHorizontally || tableMovedVertically) { + qCDebug(lcTableViewDelegateLifecycle) << "move table to" << loadedTableOuterRect; + + // relayoutTableItems() will take care of moving the existing + // delegate items into the new loadedTableOuterRect. + relayoutTableItems(); + + // Inform the sync children that they need to rebuild to stay in sync + for (auto syncChild : qAsConst(syncChildren)) { + auto syncChild_d = syncChild->d_func(); + syncChild_d->scheduledRebuildOptions |= RebuildOption::ViewportOnly; + if (tableMovedHorizontally) + syncChild_d->scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftColumn; + if (tableMovedVertically) + syncChild_d->scheduledRebuildOptions |= RebuildOption::CalculateNewTopLeftRow; } } - if (layoutNeeded) { - qCDebug(lcTableViewDelegateLifecycle); - relayoutTableItems(); + if (hData.minExtentDirty || vData.minExtentDirty) { + qCDebug(lcTableViewDelegateLifecycle) << "move origin and endExtent to:" << origin << endExtent; + // updateBeginningEnd() will let the new extents take effect. This will also change the + // visualArea of the flickable, which again will cause any attached scrollbars to adjust + // the position of the handle. Note the latter will cause the viewport to move once more. + updateBeginningEnd(); } } @@ -1413,7 +1542,6 @@ void QQuickTableViewPrivate::processLoadRequest() switch (loadRequest.edge()) { case Qt::LeftEdge: case Qt::TopEdge: - enforceTableAtOrigin(); break; case Qt::RightEdge: updateAverageEdgeSize(); @@ -1424,6 +1552,7 @@ void QQuickTableViewPrivate::processLoadRequest() updateContentHeight(); break; } + updateExtents(); drainReusePoolAfterLoadRequest(); } @@ -1638,6 +1767,14 @@ void QQuickTableViewPrivate::beginRebuildTable() else if (rebuildOptions & RebuildOption::ViewportOnly) releaseLoadedItems(reusableFlag); + if (rebuildOptions & RebuildOption::All) { + origin = QPointF(0, 0); + endExtent = QSizeF(0, 0); + hData.markExtentsDirty(); + vData.markExtentsDirty(); + updateBeginningEnd(); + } + loadedColumns.clear(); loadedRows.clear(); loadedTableOuterRect = QRect(); @@ -1704,6 +1841,7 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() updateAverageEdgeSize(); updateContentWidth(); updateContentHeight(); + updateExtents(); } void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) @@ -2283,7 +2421,6 @@ void QQuickTableViewPrivate::modelResetCallback() void QQuickTableViewPrivate::scheduleRebuildIfFastFlick() { Q_Q(QQuickTableView); - // If the viewport has moved more than one page vertically or horizontally, we switch // strategy from refilling edges around the current table to instead rebuild the table // from scratch inside the new viewport. This will greatly improve performance when flicking @@ -2374,6 +2511,26 @@ QQuickTableView::QQuickTableView(QQuickTableViewPrivate &dd, QQuickItem *parent) setFlag(QQuickItem::ItemIsFocusScope); } +qreal QQuickTableView::minXExtent() const +{ + return QQuickFlickable::minXExtent() - d_func()->origin.x(); +} + +qreal QQuickTableView::maxXExtent() const +{ + return QQuickFlickable::maxXExtent() - d_func()->endExtent.width(); +} + +qreal QQuickTableView::minYExtent() const +{ + return QQuickFlickable::minYExtent() - d_func()->origin.y(); +} + +qreal QQuickTableView::maxYExtent() const +{ + return QQuickFlickable::maxYExtent() - d_func()->endExtent.height(); +} + int QQuickTableView::rows() const { return d_func()->tableSize.height(); diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index 3d46221574..3b113efa4f 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -147,6 +147,11 @@ private: Q_DISABLE_COPY(QQuickTableView) Q_DECLARE_PRIVATE(QQuickTableView) + qreal minXExtent() const override; + qreal maxXExtent() const override; + qreal minYExtent() const override; + qreal maxYExtent() const override; + Q_PRIVATE_SLOT(d_func(), void _q_componentFinalized()) }; diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 748a1478ec..b66ac66dec 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -251,6 +251,9 @@ public: QRectF loadedTableOuterRect; QRectF loadedTableInnerRect; + QPointF origin = QPointF(0, 0); + QSizeF endExtent = QSizeF(0, 0); + QRectF viewportRect = QRectF(0, 0, -1, -1); QSize tableSize; @@ -350,7 +353,7 @@ public: void updateAverageEdgeSize(); void forceLayout(); - void enforceTableAtOrigin(); + void updateExtents(); void syncLoadedTableRectFromLoadedTable(); void syncLoadedTableFromLoadRequest(); -- cgit v1.2.3 From 23f78b6b76fb9350a472485e34857e1a4842e5d3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Mar 2019 22:40:54 +0100 Subject: TextEdit: use I-beam cursor by default, pointing cursor for links But do not interfere with any custom cursor that user code sets: remember and restore it when the mouse is no longer hovering a link. Task-number: QTBUG-14769 Fixes: QTBUG-50482 Change-Id: Ia4633c22d0ad42d07203d4dc3e330b90a5f94a7c Reviewed-by: Mitch Curtis --- src/quick/items/qquicktextedit.cpp | 19 ++++++++++++++++++- src/quick/items/qquicktextedit_p.h | 1 + src/quick/items/qquicktextedit_p_p.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 06a0fc396b..8551c008cb 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2297,7 +2297,6 @@ void QQuickTextEditPrivate::init() qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged())); qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate())); qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString))); - qmlobject_connect(control, QQuickTextControl, SIGNAL(linkHovered(QString)), q, QQuickTextEdit, SIGNAL(linkHovered(QString))); qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool))); qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged())); qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged())); @@ -2309,6 +2308,7 @@ void QQuickTextEditPrivate::init() qmlobject_connect(document, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()), q, QQuickTextEdit, SLOT(updateSize())); QObject::connect(document, &QQuickTextDocumentWithImageResources::contentsChange, q, &QQuickTextEdit::q_contentsChange); QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock); + QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered); document->setDefaultFont(font); document->setDocumentMargin(textMargin); @@ -2316,6 +2316,9 @@ void QQuickTextEditPrivate::init() document->setUndoRedoEnabled(true); updateDefaultTextOption(); q->updateSize(); +#if QT_CONFIG(cursor) + q->setCursor(Qt::IBeamCursor); +#endif } void QQuickTextEditPrivate::resetInputMethod() @@ -2583,6 +2586,20 @@ void QQuickTextEdit::updateCursor() } } +void QQuickTextEdit::q_linkHovered(const QString &link) +{ + Q_D(QQuickTextEdit); + emit linkHovered(link); +#if QT_CONFIG(cursor) + if (link.isEmpty()) { + setCursor(d->cursorToRestoreAfterHover); + } else if (cursor().shape() != Qt::PointingHandCursor) { + d->cursorToRestoreAfterHover = cursor().shape(); + setCursor(Qt::PointingHandCursor); + } +#endif +} + void QQuickTextEdit::q_updateAlignment() { Q_D(QQuickTextEdit); diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 7a847ffeae..259a614d6b 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -374,6 +374,7 @@ private Q_SLOTS: void updateWholeDocument(); void invalidateBlock(const QTextBlock &block); void updateCursor(); + void q_linkHovered(const QString &link); void q_updateAlignment(); void updateSize(); void triggerPreprocess(); diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 46d3d5ff6b..389ce3175c 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -207,6 +207,7 @@ public: Qt::InputMethodHints inputMethodHints; #endif UpdateType updateType; + Qt::CursorShape cursorToRestoreAfterHover = Qt::IBeamCursor; bool dirty : 1; bool richText : 1; -- cgit v1.2.3 From 56fbc277a1acc49d9ead4c89edd250a021ef2a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 14 May 2019 12:13:01 +0200 Subject: Do not synthesize a double click event if the event point moved too far We need to respect QPlatformTheme::TouchDoubleTapDistance Fixes: QTBUG-75770 Change-Id: I2adc7097bb29cb93beb2609a8a806a666856a0c8 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickwindow.cpp | 42 ++++++++++++++++++++++++++-------------- src/quick/items/qquickwindow_p.h | 3 ++- 2 files changed, 29 insertions(+), 16 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index f705f132fc..c55db04566 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -632,25 +633,28 @@ static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::Touc return me; } -bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp) +bool QQuickWindowPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos) { - bool doubleClicked; + bool doubleClicked = false; + + if (touchMousePressTimestamp > 0) { + QPoint distanceBetweenPresses = newPressPos - touchMousePressPos; + const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt(); + doubleClicked = (qAbs(distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(distanceBetweenPresses.y()) <= doubleTapDistance); - if (touchMousePressTimestamp == 0) { - // just initialize the variable - touchMousePressTimestamp = newPressEventTimestamp; - doubleClicked = false; - } else { - ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp; - ulong doubleClickInterval = static_cast(QGuiApplication::styleHints()-> - mouseDoubleClickInterval()); - doubleClicked = timeBetweenPresses < doubleClickInterval; if (doubleClicked) { - touchMousePressTimestamp = 0; - } else { - touchMousePressTimestamp = newPressEventTimestamp; + ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp; + ulong doubleClickInterval = static_cast(QGuiApplication::styleHints()-> + mouseDoubleClickInterval()); + doubleClicked = timeBetweenPresses < doubleClickInterval; } } + if (doubleClicked) { + touchMousePressTimestamp = 0; + } else { + touchMousePressTimestamp = newPressEventTimestamp; + touchMousePressPos = newPressPos; + } return doubleClicked; } @@ -707,7 +711,9 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (auto pointerEventPoint = pointerEvent->pointById(p.id())) pointerEventPoint->setGrabberItem(item); - if (checkIfDoubleClicked(event->timestamp())) { + if (checkIfDoubleTapped(event->timestamp(), p.screenPos().toPoint())) { + // since we synth the mouse event from from touch, we respect the + // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance QScopedPointer mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false)); QCoreApplication::sendEvent(item, mouseDoubleClick.data()); event->setAccepted(mouseDoubleClick->isAccepted()); @@ -722,6 +728,12 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve // Touch point was there before and moved } else if (touchMouseDevice == device && p.id() == touchMouseId) { if (p.state() & Qt::TouchPointMoved) { + if (touchMousePressTimestamp != 0) { + const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt(); + const QPoint moveDelta = p.screenPos().toPoint() - touchMousePressPos; + if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance) + touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap + } if (QQuickItem *mouseGrabberItem = q->mouseGrabberItem()) { QScopedPointer me(touchToMouseEvent(QEvent::MouseMove, p, event.data(), mouseGrabberItem, false)); QCoreApplication::sendEvent(item, me.data()); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 5a3807b24f..63760a3b68 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -135,8 +135,9 @@ public: #endif int touchMouseId; QQuickPointerDevice *touchMouseDevice; - bool checkIfDoubleClicked(ulong newPressEventTimestamp); + bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos); ulong touchMousePressTimestamp; + QPoint touchMousePressPos; // in screen coordiantes void cancelTouchMouseSynthesis(); // Mouse positions are saved in widget coordinates -- cgit v1.2.3 From 9b36512b9453f429644b0c388d381f7a2fc0f825 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 13 May 2019 17:48:42 +0200 Subject: Avoid illegal downcasts in texture and shader handling Fixes bad type-cast during destruction when objects no longer has the full type they used to. Detected by UBSAN. Change-Id: I4867091901d70d5a882656834eb97a704def1751 Reviewed-by: Simon Hausmann --- src/quick/items/qquickopenglshadereffect.cpp | 7 +++---- src/quick/items/qquickopenglshadereffect_p.h | 2 +- src/quick/items/qquickopenglshadereffectnode.cpp | 8 ++++---- src/quick/items/qquickopenglshadereffectnode_p.h | 4 ++-- src/quick/items/qquickshadereffect.cpp | 3 ++- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 3aa00340b2..76cb3ace5c 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -221,7 +221,7 @@ QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon() clearSignalMappers(shaderType); } -void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QObject *obj, Key::ShaderType shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { if (signalMappers[shaderType].at(i) == 0) @@ -229,12 +229,11 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, const UniformData &d = uniformData[shaderType].at(i); auto mapper = signalMappers[shaderType].at(i); void *a = mapper; - QObjectPrivate::disconnect(item, mapper->signalIndex(), &a); + QObjectPrivate::disconnect(obj, mapper->signalIndex(), &a); if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast(qvariant_cast(d.value)); if (source) { - if (item->window()) - QQuickItemPrivate::get(source)->derefWindow(); + QQuickItemPrivate::get(source)->derefWindow(); QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*))); } } diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 0c2adadc62..3087c1eb0b 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -89,7 +89,7 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon ~QQuickOpenGLShaderEffectCommon(); - void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void disconnectPropertySignals(QObject *item, Key::ShaderType shaderType); void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType); void updateParseLog(bool ignoreAttributes); void lookThroughShaderCode(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType, const QByteArray &code); diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index f32b32491b..f96ebebcd6 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -477,11 +477,11 @@ void QQuickOpenGLShaderEffectMaterial::updateTextures() const } } -void QQuickOpenGLShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) +void QQuickOpenGLShaderEffectMaterial::invalidateTextureProvider(const QObject *provider) { for (int i = 0; i < textureProviders.size(); ++i) { if (provider == textureProviders.at(i)) - textureProviders[i] = 0; + textureProviders[i] = nullptr; } } @@ -505,10 +505,10 @@ void QQuickOpenGLShaderEffectNode::markDirtyTexture() Q_EMIT dirtyTexture(); } -void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(QObject *object) +void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(const QObject *object) { Q_ASSERT(material()); - static_cast(material())->invalidateTextureProvider(static_cast(object)); + static_cast(material())->invalidateTextureProvider(object); } void QQuickOpenGLShaderEffectNode::preprocess() diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index 7c75bb3126..6d68ba87b9 100644 --- a/src/quick/items/qquickopenglshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -122,7 +122,7 @@ public: void setProgramSource(const QQuickOpenGLShaderEffectMaterialKey &source); void updateTextures() const; - void invalidateTextureProvider(QSGTextureProvider *provider); + void invalidateTextureProvider(const QObject *provider); static void cleanupMaterialCache(); @@ -159,7 +159,7 @@ Q_SIGNALS: private Q_SLOTS: void markDirtyTexture(); - void textureProviderDestroyed(QObject *object); + void textureProviderDestroyed(const QObject *object); }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index ab79b69c8c..05d9e5e36d 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -795,7 +795,8 @@ bool QQuickShaderEffect::event(QEvent *e) return QQuickItem::event(e); } #endif - m_impl->handleEvent(e); + if (m_impl) + m_impl->handleEvent(e); return QQuickItem::event(e); } -- cgit v1.2.3 From e9852df2d7c1064c95ff4c4463587ad713e68334 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 6 May 2019 17:07:21 +0200 Subject: QQuickTableView: don't recalculate content width while flicking There are now three mechanisms in TableView that works together to ensure that the table ends up edge-to-edge with the content view. They are applied in the following order: 1. Adjust the content size, based on the predicted size of the table. 2. Adjust the origin and endExtend on the fly, if the content size is wrong. 3. Move the table directly to where it should be, in case we don't have time to wait for the origin to change. We could have, strictly speaking, setteled with just one of them, but choose to use them all at the same time for best flicking experience. Still, 1. and 2. sometimes step on each others feet when they both detect that something is a bit off, and adjust. So rather than adjusting the size of the content view every time we load a new row or column, we just keep the first prediction. And then we leave all later ajustments to 2. and 3. This turns out to be a more stable, and will avoid some glitches that occur when flicking using a scrollbar, if several mechanisms kick in at the same time. Change-Id: Ib551a0bf8f6ee59ac9b3556b9462c91adb9cc80b Reviewed-by: Mitch Curtis --- src/quick/items/qquicktableview.cpp | 45 ++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 26 deletions(-) (limited to 'src/quick/items') diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 2b49200eaa..9583ef4231 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -305,9 +305,7 @@ \l view, which means that the table's width could be larger or smaller than the viewport width. As a TableView cannot always know the exact width of the table without loading all columns in the model, the \c contentWidth is - usually an estimate based on the columns it has seen so far. This estimate - is recalculated whenever new columns are flicked into view, which means - that the content width can change dynamically. + usually an estimate based on the initially loaded table. If you know what the width of the table will be, assign a value to \c contentWidth, to avoid unnecessary calculations and updates to the @@ -324,9 +322,7 @@ \c view, which means that the table's height could be larger or smaller than the viewport height. As a TableView cannot always know the exact height of the table without loading all rows in the model, the \c contentHeight is - usually an estimate based on the rows it has seen so far. This estimate is - recalculated whenever new rows are flicked into view, which means that - the content height can change dynamically. + usually an estimate based on the initially loaded table. If you know what the height of the table will be, assign a value to \c contentHeight, to avoid unnecessary calculations and updates to @@ -1539,19 +1535,6 @@ void QQuickTableViewPrivate::processLoadRequest() if (rebuildState == RebuildState::Done) { // Loading of this edge was not done as a part of a rebuild, but // instead as an incremental build after e.g a flick. - switch (loadRequest.edge()) { - case Qt::LeftEdge: - case Qt::TopEdge: - break; - case Qt::RightEdge: - updateAverageEdgeSize(); - updateContentWidth(); - break; - case Qt::BottomEdge: - updateAverageEdgeSize(); - updateContentHeight(); - break; - } updateExtents(); drainReusePoolAfterLoadRequest(); } @@ -1838,9 +1821,23 @@ void QQuickTableViewPrivate::layoutAfterLoadingInitialTable() syncLoadedTableRectFromLoadedTable(); } - updateAverageEdgeSize(); - updateContentWidth(); - updateContentHeight(); + if (syncView || rebuildOptions.testFlag(RebuildOption::All)) { + // We try to limit how often we update the content size. The main reason is that is has a + // tendency to cause flicker in the viewport if it happens while flicking. But another just + // as valid reason is that we actually never really know what the size of the full table will + // ever be. Even if e.g spacing changes, and we normally would assume that the size of the table + // would increase accordingly, the model might also at some point have removed/hidden/resized + // rows/columns outside the viewport. This would also affect the size, but since we don't load + // rows or columns outside the viewport, this information is ignored. And even if we did, we + // might also have been fast-flicked to a new location at some point, and started a new rebuild + // there based on a new guesstimated top-left cell. Either way, changing the content size + // based on the currently visible row/columns/spacing can be really off. So instead of pretending + // that we know what the actual size of the table is, we just keep the first guesstimate. + updateAverageEdgeSize(); + updateContentWidth(); + updateContentHeight(); + } + updateExtents(); } @@ -1856,8 +1853,6 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) unloadItem(QPoint(column, r.key())); loadedColumns.remove(column); syncLoadedTableRectFromLoadedTable(); - updateAverageEdgeSize(); - updateContentWidth(); break; } case Qt::TopEdge: case Qt::BottomEdge: { @@ -1866,8 +1861,6 @@ void QQuickTableViewPrivate::unloadEdge(Qt::Edge edge) unloadItem(QPoint(c.key(), row)); loadedRows.remove(row); syncLoadedTableRectFromLoadedTable(); - updateAverageEdgeSize(); - updateContentHeight(); break; } } -- cgit v1.2.3