| 237 | } |
| 238 | |
| 239 | void SourceTimelineController::applyAlignment(AlignMode mode) { |
| 240 | // Compute aligned offsets via the math engine over the current track list, |
| 241 | // then write each through the SessionManager. The widget is intentionally |
| 242 | // stateless about offsets, so the controller owns this computation. |
| 243 | TimelineScene scene; |
| 244 | std::vector<TimelineSpanInput> spans; |
| 245 | spans.reserve(tracks_.size()); |
| 246 | for (const TimelineTrack& t : tracks_) { |
| 247 | spans.push_back( |
| 248 | TimelineSpanInput{.id = t.id, .t_min_ns = t.t_min_ns, .t_max_ns = t.t_max_ns, .offset_ns = t.offset_ns}); |
| 249 | } |
| 250 | scene.setTracks(std::move(spans)); |
| 251 | std::vector<std::pair<TimelineSourceId, qint64>> offsets; |
| 252 | switch (mode) { |
| 253 | case AlignMode::kStarts: |
| 254 | offsets = scene.alignStartsToCommonOrigin(); |
| 255 | break; |
| 256 | case AlignMode::kCenters: |
| 257 | offsets = scene.alignCentersToCommonOrigin(); |
| 258 | break; |
| 259 | case AlignMode::kEnds: |
| 260 | offsets = scene.alignEndsToCommonOrigin(); |
| 261 | break; |
| 262 | } |
| 263 | SessionManager& sessions = session_->sessionManager(); |
| 264 | for (const auto& [id, offset] : offsets) { |
| 265 | sessions.setDisplayOffset(static_cast<DatasetId>(id), DisplayOffset{Duration{offset}}); |
| 266 | } |
| 267 | scheduleRangeRecompute(); |
| 268 | widget_->fitToContents(); // re-frame the realigned extent (no-op if auto-zoom is off) |
| 269 | } |
| 270 | |
| 271 | void SourceTimelineController::scheduleRangeRecompute() { |
| 272 | if (!range_recompute_timer_->isActive()) { |
nothing calls this directly
no test coverage detected