summaryrefslogtreecommitdiffstats
path: root/src/datavisualization
diff options
context:
space:
mode:
Diffstat (limited to 'src/datavisualization')
-rw-r--r--src/datavisualization/data/abstractitemmodelhandler.cpp85
-rw-r--r--src/datavisualization/data/abstractitemmodelhandler_p.h1
-rw-r--r--src/datavisualization/data/qitemmodelscatterdataproxy.cpp4
-rw-r--r--src/datavisualization/data/scatteritemmodelhandler.cpp95
-rw-r--r--src/datavisualization/data/scatteritemmodelhandler_p.h12
-rw-r--r--src/datavisualization/engine/abstract3dcontroller.cpp12
-rw-r--r--src/datavisualization/engine/abstract3dcontroller_p.h3
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp29
-rw-r--r--src/datavisualization/engine/scatter3dcontroller.cpp73
-rw-r--r--src/datavisualization/engine/scatter3dcontroller_p.h27
-rw-r--r--src/datavisualization/engine/surface3dcontroller.cpp21
11 files changed, 273 insertions, 89 deletions
diff --git a/src/datavisualization/data/abstractitemmodelhandler.cpp b/src/datavisualization/data/abstractitemmodelhandler.cpp
index cf87c3f9..879ce086 100644
--- a/src/datavisualization/data/abstractitemmodelhandler.cpp
+++ b/src/datavisualization/data/abstractitemmodelhandler.cpp
@@ -23,7 +23,8 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
AbstractItemModelHandler::AbstractItemModelHandler(QObject *parent)
: QObject(parent),
- resolvePending(0)
+ resolvePending(0),
+ m_fullReset(true)
{
m_resolveTimer.setSingleShot(true);
QObject::connect(&m_resolveTimer, &QTimer::timeout,
@@ -81,9 +82,12 @@ void AbstractItemModelHandler::handleColumnsInserted(const QModelIndex &parent,
Q_UNUSED(start)
Q_UNUSED(end)
- // Resolve new items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Manipulating columns changes all rows in proxies that map rows/columns directly,
+ // and its effects are not clearly defined in others -> always do full reset.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleColumnsMoved(const QModelIndex &sourceParent,
@@ -98,9 +102,12 @@ void AbstractItemModelHandler::handleColumnsMoved(const QModelIndex &sourceParen
Q_UNUSED(destinationParent)
Q_UNUSED(destinationColumn)
- // Resolve moved items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Manipulating columns changes all rows in proxies that map rows/columns directly,
+ // and its effects are not clearly defined in others -> always do full reset.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleColumnsRemoved(const QModelIndex &parent,
@@ -110,9 +117,12 @@ void AbstractItemModelHandler::handleColumnsRemoved(const QModelIndex &parent,
Q_UNUSED(start)
Q_UNUSED(end)
- // Remove old items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Manipulating columns changes all rows in proxies that map rows/columns directly,
+ // and its effects are not clearly defined in others -> always do full reset.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
@@ -123,9 +133,13 @@ void AbstractItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
Q_UNUSED(bottomRight)
Q_UNUSED(roles)
- // Resolve changed items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Default handling for dataChanged is to do full reset, as it cannot be optimized
+ // in a general case, where we do not know which row/column/index the item model item
+ // actually ended up to in the proxy.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleLayoutChanged(const QList<QPersistentModelIndex> &parents,
@@ -134,16 +148,20 @@ void AbstractItemModelHandler::handleLayoutChanged(const QList<QPersistentModelI
Q_UNUSED(parents)
Q_UNUSED(hint)
- // Resolve moved items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Resolve entire model if layout changes
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleModelReset()
{
// Data cleared, reset array
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleRowsInserted(const QModelIndex &parent, int start, int end)
@@ -152,9 +170,13 @@ void AbstractItemModelHandler::handleRowsInserted(const QModelIndex &parent, int
Q_UNUSED(start)
Q_UNUSED(end)
- // Resolve new items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Default handling for rowsInserted is to do full reset, as it cannot be optimized
+ // in a general case, where we do not know which row/column/index the item model item
+ // actually ended up to in the proxy.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleRowsMoved(const QModelIndex &sourceParent,
@@ -169,9 +191,13 @@ void AbstractItemModelHandler::handleRowsMoved(const QModelIndex &sourceParent,
Q_UNUSED(destinationParent)
Q_UNUSED(destinationRow)
- // Resolve moved items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Default handling for rowsMoved is to do full reset, as it cannot be optimized
+ // in a general case, where we do not know which row/column/index the item model item
+ // actually ended up to in the proxy.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleRowsRemoved(const QModelIndex &parent, int start, int end)
@@ -180,9 +206,13 @@ void AbstractItemModelHandler::handleRowsRemoved(const QModelIndex &parent, int
Q_UNUSED(start)
Q_UNUSED(end)
- // Resolve removed items
- if (!m_resolveTimer.isActive())
- m_resolveTimer.start(0); // TODO Resolving entire model is inefficient
+ // Default handling for rowsRemoved is to do full reset, as it cannot be optimized
+ // in a general case, where we do not know which row/column/index the item model item
+ // actually ended up to in the proxy.
+ if (!m_resolveTimer.isActive()) {
+ m_fullReset = true;
+ m_resolveTimer.start(0);
+ }
}
void AbstractItemModelHandler::handleMappingChanged()
@@ -194,6 +224,7 @@ void AbstractItemModelHandler::handleMappingChanged()
void AbstractItemModelHandler::handlePendingResolve()
{
resolveModel();
+ m_fullReset = false;
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/abstractitemmodelhandler_p.h b/src/datavisualization/data/abstractitemmodelhandler_p.h
index f3b72228..daaf9906 100644
--- a/src/datavisualization/data/abstractitemmodelhandler_p.h
+++ b/src/datavisualization/data/abstractitemmodelhandler_p.h
@@ -74,6 +74,7 @@ protected:
QPointer<const QAbstractItemModel> m_itemModel; // Not owned
bool resolvePending;
QTimer m_resolveTimer;
+ bool m_fullReset;
private:
Q_DISABLE_COPY(AbstractItemModelHandler)
diff --git a/src/datavisualization/data/qitemmodelscatterdataproxy.cpp b/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
index 74979309..16a7c8f7 100644
--- a/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
+++ b/src/datavisualization/data/qitemmodelscatterdataproxy.cpp
@@ -32,7 +32,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* for Q3DScatter. It maps roles of QAbstractItemModel to the XYZ-values of Q3DScatter points.
*
* The data is resolved asynchronously whenever the mapping or the model changes.
- * QScatterDataProxy::arrayReset() is emitted when the data has been resolved.
+ * QScatterDataProxy::arrayReset() is emitted when the data has been resolved. However, inserts,
+ * removes, and single data item changes after the model initialization are resolved synchronously,
+ * unless the same frame also contains a change that causes the whole model to be resolved.
*
* Mapping ignores rows and columns of the QAbstractItemModel and treats
* all items equally. It requires the model to provide at least three roles for the data items
diff --git a/src/datavisualization/data/scatteritemmodelhandler.cpp b/src/datavisualization/data/scatteritemmodelhandler.cpp
index 2faa02a9..81ecf8b0 100644
--- a/src/datavisualization/data/scatteritemmodelhandler.cpp
+++ b/src/datavisualization/data/scatteritemmodelhandler.cpp
@@ -21,6 +21,8 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+static const int noRoleIndex = -1;
+
ScatterItemModelHandler::ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent)
: AbstractItemModelHandler(parent),
m_proxy(proxy),
@@ -32,6 +34,79 @@ ScatterItemModelHandler::~ScatterItemModelHandler()
{
}
+void ScatterItemModelHandler::handleDataChanged(const QModelIndex &topLeft,
+ const QModelIndex &bottomRight,
+ const QVector<int> &roles)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (m_itemModel->columnCount() > 1) {
+ // If the data model is multi-column, do full asynchronous reset to simplify things
+ AbstractItemModelHandler::handleDataChanged(topLeft, bottomRight, roles);
+ } else {
+ int start = qMin(topLeft.row(), bottomRight.row());
+ int end = qMax(topLeft.row(), bottomRight.row());
+
+ QScatterDataArray array(end - start + 1);
+ int count = 0;
+ for (int i = start; i <= end; i++)
+ modelPosToScatterItem(i, 0, array[count++]);
+
+ m_proxy->setItems(start, array);
+ }
+ }
+}
+
+void ScatterItemModelHandler::handleRowsInserted(const QModelIndex &parent, int start, int end)
+{
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (!m_proxy->itemCount() || m_itemModel->columnCount() > 1) {
+ // If inserting into an empty array, do full asynchronous reset to avoid multiple
+ // separate inserts when initializing the model.
+ // If the data model is multi-column, do full asynchronous reset to simplify things
+ AbstractItemModelHandler::handleRowsInserted(parent, start, end);
+ } else {
+ QScatterDataArray array(end - start + 1);
+ int count = 0;
+ for (int i = start; i <= end; i++)
+ modelPosToScatterItem(i, 0, array[count++]);
+
+ m_proxy->insertItems(start, array);
+ }
+ }
+}
+
+void ScatterItemModelHandler::handleRowsRemoved(const QModelIndex &parent, int start, int end)
+{
+ Q_UNUSED(parent)
+
+ // Do nothing if full reset already pending
+ if (!m_fullReset) {
+ if (m_itemModel->columnCount() > 1) {
+ // If the data model is multi-column, do full asynchronous reset to simplify things
+ AbstractItemModelHandler::handleRowsRemoved(parent, start, end);
+ } else {
+ m_proxy->removeItems(start, end - start + 1);
+ }
+ }
+}
+
+void ScatterItemModelHandler::modelPosToScatterItem(int modelRow, int modelColumn, QScatterDataItem &item)
+{
+ QModelIndex index = m_itemModel->index(modelRow, modelColumn);
+ float xPos(0.0f);
+ float yPos(0.0f);
+ float zPos(0.0f);
+ if (m_xPosRole != noRoleIndex)
+ xPos = index.data(m_xPosRole).toFloat();
+ if (m_yPosRole != noRoleIndex)
+ yPos = index.data(m_yPosRole).toFloat();
+ if (m_zPosRole != noRoleIndex)
+ zPos = index.data(m_zPosRole).toFloat();
+ item.setPosition(QVector3D(xPos, yPos, zPos));
+}
+
// Resolve entire item model into QScatterDataArray.
void ScatterItemModelHandler::resolveModel()
{
@@ -41,12 +116,10 @@ void ScatterItemModelHandler::resolveModel()
return;
}
- static const int noRoleIndex = -1;
-
QHash<int, QByteArray> roleHash = m_itemModel->roleNames();
- const int xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
- const int yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), noRoleIndex);
- const int zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
+ m_xPosRole = roleHash.key(m_proxy->xPosRole().toLatin1(), noRoleIndex);
+ m_yPosRole = roleHash.key(m_proxy->yPosRole().toLatin1(), noRoleIndex);
+ m_zPosRole = roleHash.key(m_proxy->zPosRole().toLatin1(), noRoleIndex);
const int columnCount = m_itemModel->columnCount();
const int rowCount = m_itemModel->rowCount();
const int totalCount = rowCount * columnCount;
@@ -59,17 +132,7 @@ void ScatterItemModelHandler::resolveModel()
// Parse data into newProxyArray
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columnCount; j++) {
- QModelIndex index = m_itemModel->index(i, j);
- float xPos(0.0f);
- float yPos(0.0f);
- float zPos(0.0f);
- if (xPosRole != noRoleIndex)
- xPos = index.data(xPosRole).toFloat();
- if (yPosRole != noRoleIndex)
- yPos = index.data(yPosRole).toFloat();
- if (zPosRole != noRoleIndex)
- zPos = index.data(zPosRole).toFloat();
- (*m_proxyArray)[runningCount].setPosition(QVector3D(xPos, yPos, zPos));
+ modelPosToScatterItem(i, j, (*m_proxyArray)[runningCount]);
runningCount++;
}
}
diff --git a/src/datavisualization/data/scatteritemmodelhandler_p.h b/src/datavisualization/data/scatteritemmodelhandler_p.h
index f54ed2c5..06927509 100644
--- a/src/datavisualization/data/scatteritemmodelhandler_p.h
+++ b/src/datavisualization/data/scatteritemmodelhandler_p.h
@@ -41,11 +41,23 @@ public:
ScatterItemModelHandler(QItemModelScatterDataProxy *proxy, QObject *parent = 0);
virtual ~ScatterItemModelHandler();
+public slots:
+ virtual void handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
+ const QVector<int> &roles = QVector<int> ());
+ virtual void handleRowsInserted(const QModelIndex &parent, int start, int end);
+ virtual void handleRowsRemoved(const QModelIndex &parent, int start, int end);
+
protected:
void virtual resolveModel();
+private:
+ void modelPosToScatterItem(int modelRow, int modelColumn, QScatterDataItem &item);
+
QItemModelScatterDataProxy *m_proxy; // Not owned
QScatterDataArray *m_proxyArray; // Not owned
+ int m_xPosRole;
+ int m_yPosRole;
+ int m_zPosRole;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp
index 9942a2fe..1efbea8d 100644
--- a/src/datavisualization/engine/abstract3dcontroller.cpp
+++ b/src/datavisualization/engine/abstract3dcontroller.cpp
@@ -30,6 +30,7 @@
#include "qabstract3dseries_p.h"
#include "thememanager_p.h"
#include "q3dscene_p.h"
+#include "q3dscene.h"
#include <QThread>
@@ -147,9 +148,7 @@ QList<QAbstract3DSeries *> Abstract3DController::seriesList()
*/
void Abstract3DController::synchDataToRenderer()
{
- // If we don't have a renderer, don't do anything
- if (!m_renderer)
- return;
+ // Subclass implementations check for renderer validity already, so no need to check here.
// If there is a pending click from renderer, handle that first.
if (m_renderer->isClickPending()) {
@@ -157,7 +156,7 @@ void Abstract3DController::synchDataToRenderer()
m_renderer->clearClickPending();
}
- // TODO: start recording inserts/removals
+ startRecordingRemovesAndInserts();
if (m_scene->d_ptr->m_sceneDirty)
m_renderer->updateScene(m_scene);
@@ -1016,6 +1015,11 @@ QCategory3DAxis *Abstract3DController::createDefaultCategoryAxis()
return defaultAxis;
}
+void Abstract3DController::startRecordingRemovesAndInserts()
+{
+ // Default implementation does nothing
+}
+
void Abstract3DController::emitNeedRender()
{
if (!m_renderPending) {
diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h
index 17c1e1fb..0b89251d 100644
--- a/src/datavisualization/engine/abstract3dcontroller_p.h
+++ b/src/datavisualization/engine/abstract3dcontroller_p.h
@@ -138,9 +138,9 @@ private:
ThemeManager *m_themeManager;
QAbstract3DGraph::SelectionFlags m_selectionMode;
QAbstract3DGraph::ShadowQuality m_shadowQuality;
- Q3DScene *m_scene;
protected:
+ Q3DScene *m_scene;
QList<QAbstract3DInputHandler *> m_inputHandlers; // List of all added input handlers
QAbstract3DInputHandler *m_activeInputHandler;
CameraHelper *m_cameraHelper;
@@ -273,6 +273,7 @@ protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
QValue3DAxis *createDefaultValueAxis();
QCategory3DAxis *createDefaultCategoryAxis();
+ virtual void startRecordingRemovesAndInserts();
private:
void setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis,
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index d85e474c..b0627377 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -70,6 +70,9 @@ void Bars3DController::initializeOpenGL()
void Bars3DController::synchDataToRenderer()
{
+ if (!isInitialized())
+ return;
+
// Background change requires reloading the meshes in bar graphs, so dirty the series visuals
if (m_themeManager->activeTheme()->d_ptr->m_dirtyBits.backgroundEnabledDirty) {
m_isSeriesVisualsDirty = true;
@@ -79,9 +82,6 @@ void Bars3DController::synchDataToRenderer()
Abstract3DController::synchDataToRenderer();
- if (!isInitialized())
- return;
-
// Notify changes to renderer
if (m_changeTracker.barSpecsChanged) {
m_renderer->updateBarSpecs(m_barThicknessRatio, m_barSpacing, m_isBarSpecRelative);
@@ -135,12 +135,8 @@ void Bars3DController::handleRowsRemoved(int startIndex, int count)
{
Q_UNUSED(startIndex)
Q_UNUSED(count)
- QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustAxisRanges();
- m_isDataDirty = true;
- }
+ QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
if (series == m_selectedBarSeries) {
// If rows removed from selected series before the selection, adjust the selection
int selectedRow = m_selectedBar.x();
@@ -154,6 +150,11 @@ void Bars3DController::handleRowsRemoved(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustAxisRanges();
+ m_isDataDirty = true;
+ }
+
emitNeedRender();
}
@@ -162,11 +163,6 @@ void Bars3DController::handleRowsInserted(int startIndex, int count)
Q_UNUSED(startIndex)
Q_UNUSED(count)
QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustAxisRanges();
- m_isDataDirty = true;
- }
-
if (series == m_selectedBarSeries) {
// If rows inserted to selected series before the selection, adjust the selection
int selectedRow = m_selectedBar.x();
@@ -176,6 +172,11 @@ void Bars3DController::handleRowsInserted(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustAxisRanges();
+ m_isDataDirty = true;
+ }
+
emitNeedRender();
}
@@ -244,8 +245,6 @@ void Bars3DController::handlePendingClick()
QPoint position = m_renderer->clickedPosition();
QBar3DSeries *series = static_cast<QBar3DSeries *>(m_renderer->clickedSeries());
- // TODO: Adjust position according to inserts/removes in the series
-
setSelectedBar(position, series);
m_renderer->resetClickedStatus();
diff --git a/src/datavisualization/engine/scatter3dcontroller.cpp b/src/datavisualization/engine/scatter3dcontroller.cpp
index 4527ac2a..6aa10a11 100644
--- a/src/datavisualization/engine/scatter3dcontroller.cpp
+++ b/src/datavisualization/engine/scatter3dcontroller.cpp
@@ -29,11 +29,14 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+static const int insertRemoveRecordReserveSize = 31;
+
Scatter3DController::Scatter3DController(QRect boundRect, Q3DScene *scene)
: Abstract3DController(boundRect, scene),
m_renderer(0),
m_selectedItem(invalidSelectionIndex()),
- m_selectedItemSeries(0)
+ m_selectedItemSeries(0),
+ m_recordInsertsAndRemoves(false)
{
// Setting a null axis creates a new default axis according to orientation and graph type.
// Note: These cannot be set in Abstract3DController constructor, as they will call virtual
@@ -62,11 +65,11 @@ void Scatter3DController::initializeOpenGL()
void Scatter3DController::synchDataToRenderer()
{
- Abstract3DController::synchDataToRenderer();
-
if (!isInitialized())
return;
+ Abstract3DController::synchDataToRenderer();
+
// Notify changes to renderer
if (m_changeTracker.selectedItemChanged) {
m_renderer->updateSelectedItem(m_selectedItem, m_selectedItemSeries);
@@ -157,11 +160,6 @@ void Scatter3DController::handleItemsRemoved(int startIndex, int count)
Q_UNUSED(count)
// TODO should dirty only affected values?
QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustValueAxisRange();
- m_isDataDirty = true;
- }
-
if (series == m_selectedItemSeries) {
// If items removed from selected series before the selection, adjust the selection
int selectedItem = m_selectedItem;
@@ -175,6 +173,16 @@ void Scatter3DController::handleItemsRemoved(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustValueAxisRange();
+ m_isDataDirty = true;
+ }
+
+ if (m_recordInsertsAndRemoves) {
+ InsertRemoveRecord record(false, startIndex, count, series);
+ m_insertRemoveRecords.append(record);
+ }
+
emitNeedRender();
}
@@ -184,11 +192,6 @@ void Scatter3DController::handleItemsInserted(int startIndex, int count)
Q_UNUSED(count)
// TODO should dirty only affected values?
QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustValueAxisRange();
- m_isDataDirty = true;
- }
-
if (series == m_selectedItemSeries) {
// If items inserted to selected series before the selection, adjust the selection
int selectedItem = m_selectedItem;
@@ -198,9 +201,33 @@ void Scatter3DController::handleItemsInserted(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustValueAxisRange();
+ m_isDataDirty = true;
+ }
+
+ if (m_recordInsertsAndRemoves) {
+ InsertRemoveRecord record(true, startIndex, count, series);
+ m_insertRemoveRecords.append(record);
+ }
+
emitNeedRender();
}
+void Scatter3DController::startRecordingRemovesAndInserts()
+{
+ m_recordInsertsAndRemoves = false;
+
+ if (m_scene->selectionQueryPosition() != Q3DScene::invalidSelectionPoint()) {
+ m_recordInsertsAndRemoves = true;
+ if (m_insertRemoveRecords.size()) {
+ m_insertRemoveRecords.clear();
+ // Reserve some space for remove/insert records to avoid unnecessary reallocations.
+ m_insertRemoveRecords.reserve(insertRemoveRecordReserveSize);
+ }
+ }
+}
+
void Scatter3DController::handleAxisAutoAdjustRangeChangedInOrientation(
QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust)
{
@@ -229,7 +256,25 @@ void Scatter3DController::handlePendingClick()
int index = m_renderer->clickedIndex();
QScatter3DSeries *series = static_cast<QScatter3DSeries *>(m_renderer->clickedSeries());
- // TODO: Adjust position according to inserts/removes in the series
+ // Adjust position according to recorded events
+ int recordCount = m_insertRemoveRecords.size();
+ if (recordCount) {
+ for (int i = 0; i < recordCount; i++) {
+ const InsertRemoveRecord &record = m_insertRemoveRecords.at(i);
+ if (series == record.m_series && record.m_startIndex <= index) {
+ if (record.m_isInsert) {
+ index += record.m_count;
+ } else {
+ if ((record.m_startIndex + record.m_count) > index) {
+ index = -1; // Selected row removed
+ break;
+ } else {
+ index -= record.m_count; // Move selected item down by amount of items removed
+ }
+ }
+ }
+ }
+ }
setSelectedItem(index, series);
diff --git a/src/datavisualization/engine/scatter3dcontroller_p.h b/src/datavisualization/engine/scatter3dcontroller_p.h
index 35f4015d..2e9ade44 100644
--- a/src/datavisualization/engine/scatter3dcontroller_p.h
+++ b/src/datavisualization/engine/scatter3dcontroller_p.h
@@ -62,6 +62,30 @@ private:
QScatter3DSeries *m_selectedItemSeries; // Points to the series for which the bar is selected
// in single series selection cases.
+ struct InsertRemoveRecord {
+ bool m_isInsert;
+ int m_startIndex;
+ int m_count;
+ QAbstract3DSeries *m_series;
+
+ InsertRemoveRecord() :
+ m_isInsert(false),
+ m_startIndex(0),
+ m_count(0),
+ m_series(0)
+ {}
+
+ InsertRemoveRecord(bool isInsert, int startIndex, int count, QAbstract3DSeries *series) :
+ m_isInsert(isInsert),
+ m_startIndex(startIndex),
+ m_count(count),
+ m_series(series)
+ {}
+ };
+
+ QVector<InsertRemoveRecord> m_insertRemoveRecords;
+ bool m_recordInsertsAndRemoves;
+
public:
explicit Scatter3DController(QRect rect, Q3DScene *scene = 0);
~Scatter3DController();
@@ -93,6 +117,9 @@ public slots:
void handleItemsRemoved(int startIndex, int count);
void handleItemsInserted(int startIndex, int count);
+protected:
+ virtual void startRecordingRemovesAndInserts();
+
private:
void adjustValueAxisRange();
diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp
index 984f65ba..f0dac44b 100644
--- a/src/datavisualization/engine/surface3dcontroller.cpp
+++ b/src/datavisualization/engine/surface3dcontroller.cpp
@@ -59,7 +59,6 @@ void Surface3DController::initializeOpenGL()
m_renderer = new Surface3DRenderer(this);
setRenderer(m_renderer);
- synchDataToRenderer();
emitNeedRender();
}
@@ -388,11 +387,6 @@ void Surface3DController::handleRowsInserted(int startIndex, int count)
Q_UNUSED(startIndex)
Q_UNUSED(count)
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustValueAxisRange();
- m_isDataDirty = true;
- }
-
if (series == m_selectedSeries) {
// If rows inserted to selected series before the selection, adjust the selection
int selectedRow = m_selectedPoint.x();
@@ -402,6 +396,11 @@ void Surface3DController::handleRowsInserted(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustValueAxisRange();
+ m_isDataDirty = true;
+ }
+
emitNeedRender();
}
@@ -410,11 +409,6 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count)
Q_UNUSED(startIndex)
Q_UNUSED(count)
QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series();
- if (series->isVisible()) {
- adjustValueAxisRange();
- m_isDataDirty = true;
- }
-
if (series == m_selectedSeries) {
// If rows removed from selected series before the selection, adjust the selection
int selectedRow = m_selectedPoint.x();
@@ -428,6 +422,11 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count)
}
}
+ if (series->isVisible()) {
+ adjustValueAxisRange();
+ m_isDataDirty = true;
+ }
+
emitNeedRender();
}