diff --git a/src/cross/remote_table/OverlayFrame.cpp b/src/cross/remote_table/OverlayFrame.cpp index bdf38776..f3411d5a 100644 --- a/src/cross/remote_table/OverlayFrame.cpp +++ b/src/cross/remote_table/OverlayFrame.cpp @@ -20,6 +20,7 @@ OverlayFrame::OverlayFrame(QWidget* parent) : QFrame(parent), ui(new Ui::OverlayFrame) { + // TODO: forward the scroll/key events to the window below ui->setupUi(this); } diff --git a/src/cross/remote_table/RemoteTable.cpp b/src/cross/remote_table/RemoteTable.cpp index 67b993ff..2ae305ae 100644 --- a/src/cross/remote_table/RemoteTable.cpp +++ b/src/cross/remote_table/RemoteTable.cpp @@ -43,36 +43,57 @@ RemoteTable::RemoteTable(QWidget* parent) setRowCount(0x2000ull); mOverlay = OverlayFrame::embed(this, false); + mScrollTimer = new QTimer(this); + mScrollTimer->setSingleShot(true); + mScrollTimer->setInterval(500); // TODO: this interval should probably be the latency + connect(mScrollTimer, &QTimer::timeout, this, [this] + { + qDebug() << "Total scroll for debounce interval: " << mScrollDelta; + uint64_t minTime = UINT64_MAX; + uint64_t maxTime = 0; + dsint totalDelta = 0; + for(const auto&[time,delta] : mScrollEvents) { + uint64_t timeMs = std::chrono::duration_cast(time.time_since_epoch()).count(); + minTime = std::min(minTime, timeMs); + maxTime = std::max(maxTime, timeMs); + totalDelta += delta; + } + auto totalMs = maxTime - minTime; + auto div = totalMs / 1000.0; + qDebug() << "time:" << totalMs << "total:" << totalDelta << "velocity:" << totalDelta / div << "(lines/second)"; + mScrollEvents.clear(); + mScrollDelta = 0; + }); } QString RemoteTable::getCellContent(duint row, duint col) { - // TODO: get data from a container that's aware of the outgoing requests - // (perhaps this should be done transparently in the request/response handling?) - auto relativeRow = row - getTableOffset(); + // Show the real row number for debugging purposes QString base; if(col == 0) { base = QString("[row: %1] ").arg(row, 5); } - if(relativeRow < mRemoteData.size()) - { - const auto & remoteRow = mRemoteData[relativeRow]; - QString data; - if(col < remoteRow.size()) - { - data = QString::fromStdString(remoteRow[col]); - } - else - { - data = "(BAD SERVER)"; - } - return base + data; - } - else + + // Ignore out of range data + dsint relativeRow = row - mRemoteTableOffset; + if(relativeRow < 0 || relativeRow >= mRemoteData.size()) { return base + "(FETCHING DATA...)"; } + + // Display the data we do have + const auto & remoteRow = mRemoteData[relativeRow]; + QString data; + if(col < remoteRow.size()) + { + data = QString::fromStdString(remoteRow[col]); + } + else + { + data = "(BAD SERVER)"; + } + return base + data; } duint RemoteTable::getCellUserdata(duint row, duint column) @@ -94,6 +115,14 @@ void RemoteTable::sortRows(duint column, bool ascending) duint RemoteTable::sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta) { + // Use a debounce timer to predict the scrolling + if(!mScrollTimer->isActive()) + { + mScrollStart = std::chrono::system_clock::now(); + } + mScrollDelta += delta; + mScrollTimer->start(); + auto actionName = [action]() { switch(action) @@ -166,14 +195,23 @@ void RemoteTable::prepareData() // TODO: use the average/median/last latency of the connection as a debouncing timer // TODO: use some basic prediction heuristics (scroll direction etc) to anticipate // the scroll (request a bigger range) - // TODO: measure the ping - // TODO: implement partial reuse of the data TableRequest r; r.offset = offset; r.lines = linesToPrint; r.scroll = 0; // TODO: unused for now, used to scroll up in disassembly (variable size) + if(mScrollDelta < 0) + { + // TODO: set scroll to -100 to get 100 extra line up + } + else if(mScrollDelta > 0) + { + // TODO: increase the lines based on scroll velocity + //r.lines *= 2; + } + //qDebug() << "scrollDelta:" << mScrollDelta; + if(!mCurrentSent) { assert(!mNextRequired); @@ -193,6 +231,14 @@ void RemoteTable::prepareData() } } +void RemoteTable::wheelEvent(QWheelEvent* event) +{ + dsint prevScrollDelta = mScrollDelta; + AbstractStdTable::wheelEvent(event); + auto now = std::chrono::system_clock::now(); + mScrollEvents.emplace_back(now, mScrollDelta - prevScrollDelta); +} + void RemoteTable::handleTableResponse(const TableResponse & response) { // Calculate response time statistics @@ -230,6 +276,8 @@ void RemoteTable::handleTableResponse(const TableResponse & response) qDebug() << "[response time] now:" << elapsedMs << "min:" << mMinResponseTime << "max:" << mMaxResponseTime << "avg:" << mAvgResponseTime << "med:" << mMedResponseTime; + // TODO: use these statistics to send a deferred request a bit earlier + // Handle deferred request if(mNextRequired) { @@ -269,6 +317,7 @@ void RemoteTable::handleTableResponse(const TableResponse & response) mOverlay->setVisible(false); // Update the displayed data with the final response + mRemoteTableOffset = mCurrentRequest.offset; mRemoteData = std::move(response.rows); updateViewport(); } diff --git a/src/cross/remote_table/RemoteTable.h b/src/cross/remote_table/RemoteTable.h index f375f1a2..587b714f 100644 --- a/src/cross/remote_table/RemoteTable.h +++ b/src/cross/remote_table/RemoteTable.h @@ -8,6 +8,7 @@ #include #include +#include #include class RemoteTable : public AbstractStdTable, public MagicMenu @@ -24,6 +25,7 @@ protected: void sortRows(duint column, bool ascending) override; duint sliderMovedHook(QScrollBar::SliderAction action, duint value, dsint delta) override; void prepareData() override; + void wheelEvent(QWheelEvent* event) override; private: enum ColumnIndex @@ -32,15 +34,18 @@ private: ColData, }; + using Epoch = std::chrono::time_point; + + duint mRemoteTableOffset = 0; std::vector> mRemoteData; - std::chrono::time_point mLastPrepare; + Epoch mLastPrepare; QWebSocket mSocket; JsonRpcClient mRpc; TableRequest mCurrentRequest; bool mCurrentSent = false; - std::chrono::time_point mCurrentSentTime; + Epoch mCurrentSentTime; TableRequest mNextRequest; bool mNextRequired = false; @@ -53,6 +58,11 @@ private: uint64_t mAvgResponseTime = 100; uint64_t mMedResponseTime = 100; + QTimer* mScrollTimer = nullptr; + dsint mScrollDelta = 0; + Epoch mScrollStart; + std::deque> mScrollEvents; + void handleTableResponse(const TableResponse & response); void setupMenu(); void setupConnection();