| 268 | } |
| 269 | |
| 270 | bool Scrollbar::eventFilter(QObject* watched, QEvent* event) { |
| 271 | if (area_ == nullptr || watched != area_->viewport()) { |
| 272 | return false; |
| 273 | } |
| 274 | |
| 275 | switch (event->type()) { |
| 276 | case QEvent::MouseButtonPress: { |
| 277 | auto* me = static_cast<QMouseEvent*>(event); |
| 278 | if (me->button() != Qt::LeftButton) { |
| 279 | break; |
| 280 | } |
| 281 | // FIX 1: when not interactive, ignore the press — do not start a drag and |
| 282 | // do not consume the event so the Timeline's own filter handles it. |
| 283 | if (!interactive_) { |
| 284 | break; |
| 285 | } |
| 286 | const QPoint pos = me->position().toPoint(); |
| 287 | if (!pointInStrip(pos) || bar()->maximum() <= bar()->minimum()) { |
| 288 | break; |
| 289 | } |
| 290 | |
| 291 | const double axis_px = (orientation_ == Qt::Horizontal) ? pos.x() : pos.y(); |
| 292 | const bool on_handle = (axis_px >= handle_pos_) && (axis_px <= handle_pos_ + handle_len_); |
| 293 | |
| 294 | // By default only the visible handle is a drag target: a press on the empty |
| 295 | // track (in the strip but off the handle) is NOT consumed, so it falls |
| 296 | // through to the underlying view — otherwise the overlay would silently |
| 297 | // swallow content clicks (item selection, etc.) along the whole edge strip, |
| 298 | // where the native bar used to reserve a non-content gutter but now does not |
| 299 | // (it is forced AlwaysOff and content reflows under the strip). A host that |
| 300 | // owns its whole viewport (the Timeline) opts into click_to_scroll_, where an |
| 301 | // off-handle press first centers the handle under the cursor, then drags. |
| 302 | if (!on_handle) { |
| 303 | if (!click_to_scroll_) { |
| 304 | break; |
| 305 | } |
| 306 | const double track_len = (orientation_ == Qt::Horizontal) ? static_cast<double>(viewport_->width()) |
| 307 | : static_cast<double>(viewport_->height()); |
| 308 | const long total = (bar()->maximum() - bar()->minimum()) + bar()->pageStep(); |
| 309 | if (track_len > 0.0) { |
| 310 | const double want_pos = std::clamp(axis_px - handle_len_ / 2.0, 0.0, std::max(0.0, track_len - handle_len_)); |
| 311 | bar()->setValue( |
| 312 | bar()->minimum() + static_cast<int>(std::llround((want_pos / track_len) * static_cast<double>(total)))); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | // Capture drag origin AFTER any click-to-center value change. |
| 317 | drag_start_value_ = bar()->value(); |
| 318 | drag_start_axis_px_ = axis_px; |
| 319 | dragging_ = true; |
| 320 | setShown(true); |
| 321 | return true; // consume: prevent viewport row-selection / pan on press |
| 322 | } |
| 323 | |
| 324 | case QEvent::MouseMove: { |
| 325 | auto* me = static_cast<QMouseEvent*>(event); |
| 326 | const QPoint pos = me->position().toPoint(); |
| 327 |
nothing calls this directly
no test coverage detected