From 793b44e5dd92095919aeeba7156c4fe222624b7f Mon Sep 17 00:00:00 2001
From: Igor Korsukov <igor.korsukov@gmail.com>
Date: Tue, 14 May 2024 23:01:43 +0300
Subject: [PATCH] [AU4] limiting wave drawing to the visible area

---
 au4/src/au3wrap/iau3wavepainter.h             | 10 ++-
 au4/src/au3wrap/internal/au3wavepainter.cpp   | 31 ++++++++--
 .../ProjectScene/clipsview/ClipItem.qml       |  2 +
 .../clipsview/TracksClipsView.qml             | 62 +++++++++++--------
 .../view/clipsview/clipslistmodel.cpp         |  6 +-
 .../view/clipsview/timelinecontext.cpp        | 13 +++-
 .../view/clipsview/timelinecontext.h          |  2 +
 .../projectscene/view/clipsview/waveview.cpp  | 38 +++++++++++-
 .../projectscene/view/clipsview/waveview.h    | 12 +++-
 9 files changed, 138 insertions(+), 38 deletions(-)

diff --git a/au4/src/au3wrap/iau3wavepainter.h b/au4/src/au3wrap/iau3wavepainter.h
index b7b7117b51..fe98e7d895 100644
--- a/au4/src/au3wrap/iau3wavepainter.h
+++ b/au4/src/au3wrap/iau3wavepainter.h
@@ -22,8 +22,16 @@ public:
         QColor highlight;
     };
 
+    struct Geometry {
+        double height = 0.0;        // wave view height
+        double width = 0.0;         // wave view width
+        double left = 0.0;          // on track line
+        double frameLeft = 0.0;     // track line shift
+        double frameWidth = 0.0;    // track line visible width
+    };
+
     struct Params {
-        QRect viewRect;
+        Geometry geometry;
         double zoom = 0.0;
         Style style;
     };
diff --git a/au4/src/au3wrap/internal/au3wavepainter.cpp b/au4/src/au3wrap/internal/au3wavepainter.cpp
index 234e397656..b6e7dd6e00 100644
--- a/au4/src/au3wrap/internal/au3wavepainter.cpp
+++ b/au4/src/au3wrap/internal/au3wavepainter.cpp
@@ -149,6 +149,13 @@ public:
         auto left = targetRect.x() + leftOffset;
         auto height = targetRect.height();
 
+        LOGDA() << "zoom: " << zoomInfo.GetZoom()
+                << " leftOffset: " << leftOffset
+                << " targetRect.x() : " << targetRect.x()
+                << " left: " << left
+                << " from: " << from
+                << " to: " << to;
+
         const auto top = targetRect.y();
 
         for (auto it = range.begin(); it != range.end(); ++it) {
@@ -888,6 +895,7 @@ void DrawWaveform(int channelIndex,
     }
 
     const double t0 = params.t0;
+    LOGDA() << "t0: " << t0;
     const double sampleRate = clip.GetRate();
     const double playStartTime = clip.GetPlayStartTime();
     const double trackRectT0 = params.trackRectT0;
@@ -1012,6 +1020,7 @@ AudacityProject& Au3WavePainter::projectRef() const
 
 void Au3WavePainter::paint(QPainter& painter, const processing::ClipKey& clipKey, const Params& params)
 {
+    LOGD() << "trackId: " << clipKey.trackId << ", clip: " << clipKey.index;
     WaveTrack* track = DomAccessor::findWaveTrack(projectRef(), TrackId(clipKey.trackId));
     IF_ASSERT_FAILED(track) {
         return;
@@ -1043,13 +1052,27 @@ void Au3WavePainter::doPaint(QPainter& painter, const WaveTrack* _track, const W
 
     const bool dB = !WaveformSettings::Get(*track).isLinear();
 
-    auto top = params.viewRect.top();
-    const auto channelHeight = (params.viewRect.height()) / static_cast<int>(clip->NChannels());
+    const auto channelHeight = (params.geometry.height) / static_cast<int>(clip->NChannels());
+
+    const Geometry& g = params.geometry;
+
+    double width = std::min(g.left + g.width, g.frameLeft + g.frameWidth) - g.left;
+    double left = std::min(g.left - g.frameLeft, 0.0);
+    double clipStartTime = clip->GetPlayStartTime();// + (left / params.zoom);
+    LOGDA() << "g.width: " << g.width
+            << " g.left: " << g.left
+            << " g.frameWidth: " << g.frameWidth
+            << " g.frameLeft: " << g.frameLeft
+            << " (g.left + g.width): " << (g.left + g.width)
+            << " (g.frameLeft + g.frameWidth): " << (g.frameLeft + g.frameWidth)
+            << " width: " << width
+            << " left: " << left;
 
-    ZoomInfo zoomInfo{ clip->GetPlayStartTime(), params.zoom };
+    ZoomInfo zoomInfo(clipStartTime, params.zoom);
     SelectedRegion selectedRegion{};
+    double top = 0.0;
     for (unsigned i = 0; i < clip->NChannels(); ++i) {
-        QRect rect = QRect(params.viewRect.left(), top, params.viewRect.width(), channelHeight);
+        QRect rect(0, top, width, channelHeight);
         DrawWaveform(i, painter, *track, *clip, zoomInfo, selectedRegion, rect, params.style, dB, track->GetMute(), false);
         top += channelHeight;
     }
diff --git a/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/ClipItem.qml b/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/ClipItem.qml
index eaed6cad03..94bd4f8db4 100644
--- a/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/ClipItem.qml
+++ b/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/ClipItem.qml
@@ -55,5 +55,7 @@ Rectangle {
         anchors.right: parent.right
         anchors.bottom: parent.bottom
         anchors.margins: 1
+
+        clipLeft: root.x
     }
 }
diff --git a/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/TracksClipsView.qml b/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/TracksClipsView.qml
index 5fc57a83fa..721fff7694 100644
--- a/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/TracksClipsView.qml
+++ b/au4/src/projectscene/qml/Audacity/ProjectScene/clipsview/TracksClipsView.qml
@@ -4,12 +4,15 @@ import Muse.UiComponents
 
 import Audacity.ProjectScene
 
-Item {
+Rectangle {
 
     id: root
 
     clip: true
 
+    //color: ui.theme.backgroundPrimaryColor
+    color: "#ffffff"
+
     TracksListClipsModel {
         id: tracksModel
     }
@@ -18,35 +21,42 @@ Item {
         tracksModel.load()
     }
 
-    Timeline {
-        id: timeline
+    Rectangle {
+        anchors.fill: parent
+        anchors.leftMargin: 150
+        anchors.rightMargin: 150
+        color: ui.theme.backgroundPrimaryColor
 
-        anchors.top: parent.top
-        anchors.left: parent.left
-        anchors.right: parent.right
-    }
+        Timeline {
+            id: timeline
 
-    MouseArea {
-        anchors.fill: parent
-        onWheel: function(wheel) {
-            timeline.onWheel(wheel.angleDelta.y)
+            anchors.top: parent.top
+            anchors.left: parent.left
+            anchors.right: parent.right
+        }
+
+        MouseArea {
+            anchors.fill: parent
+            onWheel: function(wheel) {
+                timeline.onWheel(wheel.angleDelta.y)
+            }
         }
-    }
 
-    Column {
-        anchors.top: timeline.bottom
-        anchors.left: parent.left
-        anchors.right: parent.right
-        anchors.bottom: parent.bottom
-
-        Repeater {
-            model: tracksModel
-            delegate: TrackClipsItem {
-                anchors.left: parent.left
-                anchors.right: parent.right
-                height: 144
-                context: timeline.context
-                trackId: trackIdData
+        Column {
+            anchors.top: timeline.bottom
+            anchors.left: parent.left
+            anchors.right: parent.right
+            anchors.bottom: parent.bottom
+
+            Repeater {
+                model: tracksModel
+                delegate: TrackClipsItem {
+                    anchors.left: parent.left
+                    anchors.right: parent.right
+                    height: 144
+                    context: timeline.context
+                    trackId: trackIdData
+                }
             }
         }
     }
diff --git a/au4/src/projectscene/view/clipsview/clipslistmodel.cpp b/au4/src/projectscene/view/clipsview/clipslistmodel.cpp
index dabc1f7e3d..4856435d20 100644
--- a/au4/src/projectscene/view/clipsview/clipslistmodel.cpp
+++ b/au4/src/projectscene/view/clipsview/clipslistmodel.cpp
@@ -91,7 +91,7 @@ QVariant ClipsListModel::data(const QModelIndex& index, int role) const
 
 bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, int role)
 {
-    LOGD() << "" << index.row() << ", value: " << value << ", role: " << role;
+    //LOGD() << "" << index.row() << ", value: " << value << ", role: " << role;
     switch (role) {
     case ClipLeftRole: {
         return changeClipStartTime(index, value);
@@ -104,6 +104,10 @@ bool ClipsListModel::setData(const QModelIndex& index, const QVariant& value, in
 
 void ClipsListModel::onTimelineContextValuesChanged()
 {
+    // LOGDA() << "zoom: " << m_context->zoom()
+    //         << " frameStartTime: " << m_context->frameStartTime()
+    //         << " frameEndTime: " << m_context->frameEndTime();
+
     for (size_t i = 0; i < m_clipList.size(); ++i) {
         QModelIndex idx = this->index(int(i));
         emit dataChanged(idx, idx, { ClipWidthRole, ClipLeftRole });
diff --git a/au4/src/projectscene/view/clipsview/timelinecontext.cpp b/au4/src/projectscene/view/clipsview/timelinecontext.cpp
index 536632b4e6..6f6e6e9dda 100644
--- a/au4/src/projectscene/view/clipsview/timelinecontext.cpp
+++ b/au4/src/projectscene/view/clipsview/timelinecontext.cpp
@@ -37,7 +37,7 @@ void TimelineContext::onWheel(double y)
 
 void TimelineContext::changeZoom(int direction)
 {
-    double step = std::max(zoom() / 2.0, ZOOM_MIN);
+    double step = std::round(std::max(zoom() / 2.0, ZOOM_MIN) * 10) / 10;
 
     double _zoom = zoom() + (step * direction);
     _zoom = std::floor(_zoom * 10.0) / 10.0;
@@ -52,8 +52,8 @@ void TimelineContext::changeZoom(int direction)
 
 void TimelineContext::onResizeFrameWidth(double frameWidth)
 {
-    setFrameEndTime(m_frameStartTime + positionToTime(frameWidth));
-    emit frameTimeChanged();
+    m_frameWidth = frameWidth;
+    updateFrameTime();
 }
 
 void TimelineContext::shiftFrameTime(int direction)
@@ -67,6 +67,12 @@ void TimelineContext::shiftFrameTime(int direction)
     emit frameTimeChanged();
 }
 
+void TimelineContext::updateFrameTime()
+{
+    setFrameEndTime(m_frameStartTime + positionToTime(m_frameWidth));
+    emit frameTimeChanged();
+}
+
 void TimelineContext::onSelection(double x1, double x2)
 {
     onSelectionTime(positionToTime(x1), positionToTime(x2));
@@ -110,6 +116,7 @@ void TimelineContext::setZoom(double zoom)
     if (m_zoom != zoom) {
         m_zoom = zoom;
         emit zoomChanged();
+        updateFrameTime();
     }
 }
 
diff --git a/au4/src/projectscene/view/clipsview/timelinecontext.h b/au4/src/projectscene/view/clipsview/timelinecontext.h
index 76bb2c1023..9524fab9ac 100644
--- a/au4/src/projectscene/view/clipsview/timelinecontext.h
+++ b/au4/src/projectscene/view/clipsview/timelinecontext.h
@@ -68,6 +68,7 @@ private:
     void shiftFrameTime(int direction);
     void setFrameStartTime(double newFrameStartTime);
     void setFrameEndTime(double newFrameEndTime);
+    void updateFrameTime();
 
     void changeZoom(int direction);
 
@@ -75,6 +76,7 @@ private:
     void updateSelectionActive();
     void setSelectionActive(bool newSelectionActive);
 
+    double m_frameWidth = 0.0;
     double m_frameStartTime = 0.0;
     double m_frameEndTime = 0.0;
 
diff --git a/au4/src/projectscene/view/clipsview/waveview.cpp b/au4/src/projectscene/view/clipsview/waveview.cpp
index 899b1f19c0..c2ef066e9f 100644
--- a/au4/src/projectscene/view/clipsview/waveview.cpp
+++ b/au4/src/projectscene/view/clipsview/waveview.cpp
@@ -29,7 +29,12 @@ void WaveView::setClipKey(const ClipKey& newClipKey)
 void WaveView::paint(QPainter* painter)
 {
     au3::IAu3WavePainter::Params params;
-    params.viewRect = QRect(0, 0, width(), height());
+    params.geometry.height = height();
+    params.geometry.width = width();
+    params.geometry.left = m_clipLeft;
+    params.geometry.frameLeft = m_context->frameStartTime() * m_context->zoom();
+    params.geometry.frameWidth = (m_context->frameEndTime() - m_context->frameStartTime()) * m_context->zoom();
+
     params.zoom = m_context->zoom();
 
     const WaveStyle& style = configuration()->waveStyle();
@@ -60,7 +65,36 @@ void WaveView::setTimelineContext(TimelineContext* newContext)
         return;
     }
 
-    //! TODO Subscribe on context props changes
+    if (m_context) {
+        disconnect(m_context, nullptr, this, nullptr);
+    }
+
     m_context = newContext;
+
+    if (m_context) {
+        connect(m_context, &TimelineContext::frameTimeChanged, this, &WaveView::onFrameTimeChanged);
+    }
+
     emit timelineContextChanged();
 }
+
+void WaveView::onFrameTimeChanged()
+{
+    update();
+}
+
+double WaveView::clipLeft() const
+{
+    return m_clipLeft;
+}
+
+void WaveView::setClipLeft(double newClipLeft)
+{
+    if (qFuzzyCompare(m_clipLeft, newClipLeft)) {
+        return;
+    }
+    m_clipLeft = newClipLeft;
+    emit clipLeftChanged();
+
+    update();
+}
diff --git a/au4/src/projectscene/view/clipsview/waveview.h b/au4/src/projectscene/view/clipsview/waveview.h
index 149b9bcf86..f65913a140 100644
--- a/au4/src/projectscene/view/clipsview/waveview.h
+++ b/au4/src/projectscene/view/clipsview/waveview.h
@@ -17,6 +17,11 @@ class WaveView : public QQuickPaintedItem
     Q_PROPERTY(TimelineContext * context READ timelineContext WRITE setTimelineContext NOTIFY timelineContextChanged FINAL)
     Q_PROPERTY(ClipKey clipKey READ clipKey WRITE setClipKey NOTIFY clipKeyChanged FINAL)
 
+    //! NOTE In a static position, the slip start time corresponds,
+    //! but it can change while dragging the clip, with the not changing start time
+    //! (until the end of dragging)
+    Q_PROPERTY(double clipLeft READ clipLeft WRITE setClipLeft NOTIFY clipLeftChanged FINAL)
+
     muse::Inject<au3::IAu3WavePainter> wavePainter;
     muse::Inject<IProjectSceneConfiguration> configuration;
 
@@ -28,17 +33,22 @@ public:
     void setClipKey(const ClipKey& newClipKey);
     TimelineContext* timelineContext() const;
     void setTimelineContext(TimelineContext* newContext);
+    double clipLeft() const;
+    void setClipLeft(double newClipLeft);
 
     void paint(QPainter* painter) override;
 
 signals:
     void clipKeyChanged();
     void timelineContextChanged();
+    void clipLeftChanged();
 
 private:
-    void UpdateItemsCache(TimelineContext& trackPanelView);
+
+    void onFrameTimeChanged();
 
     ClipKey m_clipKey;
     TimelineContext* m_context = nullptr;
+    double m_clipLeft = 0;
 };
 }
-- 
GitLab