| 2980 | } |
| 2981 | |
| 2982 | void MainWindow::onProgressiveLayoutDrained() { |
| 2983 | QObject::disconnect(pending_items_added_conn_); |
| 2984 | pending_items_added_conn_ = {}; |
| 2985 | |
| 2986 | if (pending_binder_ != nullptr) { |
| 2987 | static_cast<void>(pending_binder_->flush({})); |
| 2988 | // Frame each restored plot to its layout-saved window with the now-settled display |
| 2989 | // offset (the file has finished loading). This previously called zoomOut because the |
| 2990 | // saved range was display-relative and the save vs reload offsets could differ; PR |
| 2991 | // #248 made the saved X ABSOLUTE and applySavedViewportOrZoom converts it with the |
| 2992 | // live offset, so re-applying it is correct — and it pins the plot to its final |
| 2993 | // window so data fills in like streaming rather than the axis auto-fitting/growing. |
| 2994 | // Plots with no/degenerate saved range fall back to zoomOut; clear_after drops the |
| 2995 | // one-shot stash. Still under the in-flight gate, so onUndoableChange stays |
| 2996 | // suppressed until the single snapshot below. |
| 2997 | forEachPlot([](PlotWidget* plot) { plot->applySavedViewportOrZoom(/*clear_after=*/true); }); |
| 2998 | |
| 2999 | QStringList shown; |
| 3000 | QSet<QString> seen; |
| 3001 | for (const layout_xml::SeriesPath& path : pending_binder_->unresolved()) { |
| 3002 | const QString display = path.display(); |
| 3003 | if (!display.isEmpty() && !seen.contains(display)) { |
| 3004 | seen.insert(display); |
| 3005 | shown.push_back(display); |
| 3006 | } |
| 3007 | } |
| 3008 | if (!shown.isEmpty()) { |
| 3009 | switch (promptMissingCurves(shown)) { |
| 3010 | case MissingCurveChoice::kRemove: |
| 3011 | break; // Remaining curves were never bound, so there is nothing to strip from live widgets. |
| 3012 | case MissingCurveChoice::kCancel: |
| 3013 | // The reload has already mutated the live session, so progressive restore is non-transactional: |
| 3014 | // leave the partial layout in place instead of rolling back to a stale pre-load snapshot. |
| 3015 | break; |
| 3016 | } |
| 3017 | } |
| 3018 | pending_binder_->clear(); |
| 3019 | } |
| 3020 | |
| 3021 | retryPendingSceneRestores({}); |
| 3022 | broadcastTrackerTime(toAxisDouble(session_->playbackEngine().currentTime())); |
| 3023 | const QStringList unresolved_scenes = unresolvedPendingSceneRestores(); |
| 3024 | if (!unresolved_scenes.isEmpty()) { |
| 3025 | emitDiagnostic( |
| 3026 | DiagnosticLevel::kWarning, "Layout", "scene_restore_pending", |
| 3027 | tr("%n scene layer(s) could not be rebound after progressive load.", nullptr, |
| 3028 | static_cast<int>(unresolved_scenes.size()))); |
| 3029 | } |
| 3030 | clearPendingSceneRestores(); |
| 3031 | |
| 3032 | QObject::disconnect(pending_queue_drained_conn_); |
| 3033 | pending_queue_drained_conn_ = {}; |
| 3034 | progressive_layout_in_flight_ = false; |
| 3035 | pushUndoState(/*force_new_state=*/true); |
| 3036 | } |
| 3037 | |
| 3038 | void MainWindow::saveLayoutToPath(const QString& path, bool include_data_source) { |
| 3039 | QDomDocument doc = xmlSaveState(); |
nothing calls this directly
no test coverage detected