diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2018-06-25 14:58:39 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2018-06-26 08:14:10 +0000 |
commit | 7045f185a1efae8dee4fcb425a0b2d92d21303ff (patch) | |
tree | b2e5927947d737287c3e94c7d4aafd14e2ebb534 /src/Authoring | |
parent | 1bde524d4583d2f3d5cae76e0836904b4228a22b (diff) |
Add autoscroll on edges for keyframe selection rectangle
Now timeline autoscrolls when dragging keyframe selection box is
dragged beyond timeline edges. Also added limiter to autoscroll speed
per frame and a distance divisor, which allows more precise scrolling.
Task-number: QT3DS-717
Change-Id: I29b992ba80fadf60bfec421cbd7e401e36b01ae1
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Jere Tuliniemi <jere.tuliniemi@qt.io>
Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src/Authoring')
6 files changed, 100 insertions, 42 deletions
diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.cpp index 39c1bc99..f04165e8 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.cpp @@ -32,7 +32,7 @@ #include <QtGui/qpainter.h> -SelectionRect::SelectionRect(Ruler *ruler) : m_ruler(ruler) +SelectionRect::SelectionRect() { setZValue(100); setActive(false); @@ -55,14 +55,17 @@ void SelectionRect::start(const QPointF &origin) m_active = true; } -void SelectionRect::updateSize(const QPointF &pos) +void SelectionRect::updateSize(const QPointF &pos, const QRectF &visibleScene) { QPointF newPos = pos; - if (newPos.x() < m_ruler->x()) - newPos.setX(m_ruler->x()); - - if (newPos.y() < TimelineConstants::ROW_H) - newPos.setY(TimelineConstants::ROW_H); + if (newPos.x() < visibleScene.left()) + newPos.setX(visibleScene.left()); + else if (newPos.x() > visibleScene.right()) + newPos.setX(visibleScene.right()); + if (newPos.y() < visibleScene.top()) + newPos.setY(visibleScene.top()); + else if (newPos.y() > visibleScene.bottom()) + newPos.setY(visibleScene.bottom()); m_rect.setBottomRight(newPos); setRect(m_rect.normalized()); diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.h index 36ea3fb7..cad62662 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/SelectionRect.h @@ -29,17 +29,15 @@ #ifndef SELECTIONRECT_H #define SELECTIONRECT_H -class Ruler; - #include <QtWidgets/qgraphicsitem.h> class SelectionRect : public QGraphicsRectItem { public: - explicit SelectionRect(Ruler *ruler); + explicit SelectionRect(); void start(const QPointF &origin); - void updateSize(const QPointF &pos); + void updateSize(const QPointF &pos, const QRectF &visibleScene); void end(); bool isActive(); @@ -47,7 +45,6 @@ public: QWidget *widget = nullptr) override; private: - Ruler *m_ruler = nullptr; bool m_active = false; QRectF m_rect; }; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h index c5233ca2..9c3ab457 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h @@ -92,6 +92,9 @@ namespace TimelineConstants const int AUTO_EXPAND_TIME = 500; // auto expand a hovered row (while DnD-ing) const double MAX_SLIDE_TIME = 3599.0; // seconds + const int TIMELINE_SCROLL_MAX_DELTA = 25; // Maximum amount of pixels to scroll per frame + const int TIMELINE_SCROLL_DIVISOR = 6; // Divisor for timeline autoscroll distance + // TODO: move the colors and dimensions to StudioPreferences. } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp index 0efaefdf..65aa65cc 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp @@ -77,7 +77,7 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) , m_layoutTimeline(new QGraphicsLinearLayout(Qt::Vertical)) , m_ruler(new Ruler) , m_playHead(new PlayHead(m_ruler)) - , m_selectionRect(new SelectionRect(m_ruler)) + , m_selectionRect(new SelectionRect()) , m_rowMover(new RowMover(this)) , m_widgetTimeline(timelineWidget) , m_widgetRoot(new QGraphicsWidget) @@ -138,26 +138,68 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) connect(&m_autoScrollTimelineTimer, &QTimer::timeout, [this]() { if (!qApp->focusWindow() && !g_StudioApp.isOnProgress()) { - m_autoScrollTimelineTimer.stop(); resetMousePressParams(); return; } + QGraphicsView *timelineContent = m_widgetTimeline->viewTimelineContent(); + QPoint scrollBarOffsets(timelineContent->verticalScrollBar()->isVisible() + ? timelineContent->verticalScrollBar()->width() : 0, + timelineContent->horizontalScrollBar()->isVisible() + ? timelineContent->horizontalScrollBar()->height() : 0); + const QRect contentRect = timelineContent->contentsRect(); + const double right = timelineContent->width() - scrollBarOffsets.x(); + QPoint p = m_widgetTimeline->mapFromGlobal(QCursor::pos()) + - QPoint(m_widgetTimeline->viewTreeContent()->width() + + TimelineConstants::SPLITTER_W, 0); + + // Limit the maximum scroll speed + if (p.x() < 0) { + p.setX(qMax(-TimelineConstants::TIMELINE_SCROLL_MAX_DELTA, + p.x() / TimelineConstants::TIMELINE_SCROLL_DIVISOR)); + } else if (p.x() > right) { + p.setX(qMin(right + TimelineConstants::TIMELINE_SCROLL_MAX_DELTA, + right + 1 + ((p.x() - right) + / TimelineConstants::TIMELINE_SCROLL_DIVISOR))); + } + + if (m_selectionRect->isActive()) { + p -= QPoint(0, m_widgetTimeline->navigationBar()->height() + + TimelineConstants::ROW_H); + const double bottom = timelineContent->contentsRect().height() - scrollBarOffsets.y(); + if (m_lastAutoScrollX != p.x() || p.x() <= 0 || p.x() >= right + || m_lastAutoScrollY != p.y() || p.y() <= 0 || p.y() >= bottom) { + m_lastAutoScrollX = p.x(); + m_lastAutoScrollY = p.y(); + + if (p.y() < 0) { + p.setY(qMax(-TimelineConstants::TIMELINE_SCROLL_MAX_DELTA, + p.y() / TimelineConstants::TIMELINE_SCROLL_DIVISOR)); + } else if (p.y() > bottom) { + p.setY(qMin(bottom + TimelineConstants::TIMELINE_SCROLL_MAX_DELTA, + bottom + 1 + ((p.y() - bottom) + / TimelineConstants::TIMELINE_SCROLL_DIVISOR))); + } - QPoint p = m_widgetTimeline->mapFromGlobal(QCursor::pos()); - double left = m_widgetTimeline->viewTreeContent()->width() - + TimelineConstants::SPLITTER_W; - double right = left + m_widgetTimeline->viewTimelineContent()->width() - - TimelineConstants::RULER_EDGE_OFFSET; - if (m_lastPlayHeadX != p.x() || p.x() < left || p.x() > right) { - m_lastPlayHeadX = p.x(); + // Resize keyframe selection rect + const QPointF scenePoint = timelineContent->mapToScene(p); + timelineContent->ensureVisible(scenePoint.x(), scenePoint.y(), + 0, 0, 0, 0); + QRectF visibleScene( + timelineContent->mapToScene(contentRect.topLeft()), + timelineContent->mapToScene(contentRect.bottomRight() + - scrollBarOffsets)); + m_selectionRect->updateSize(scenePoint, visibleScene); + m_keyframeManager->selectKeyframesInRect(m_selectionRect->rect()); + } + } else if (m_lastAutoScrollX != p.x() || p.x() <= 0 || p.x() >= right) { + m_lastAutoScrollX = p.x(); bool shift = QGuiApplication::queryKeyboardModifiers() & Qt::ShiftModifier; - double scroll = m_widgetTimeline->viewTimelineContent() - ->horizontalScrollBar()->value(); + double scroll = timelineContent->horizontalScrollBar()->value(); if (scroll != 0) scroll -= TimelineConstants::TREE_BOUND_W; - double distance = p.x() - left + scroll; + double distance = p.x() + scroll; if (m_clickedTimelineControlType == TimelineControlType::Duration) distance -= m_editedTimelineRow->getDurationMoveOffsetX(); @@ -178,9 +220,8 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) m_editedTimelineRow->setStartX(distance); m_editedTimelineRow->showToolTip(QCursor::pos()); - m_widgetTimeline->viewTimelineContent()->ensureVisible( - TimelineConstants::TREE_BOUND_W + visiblePtX, - m_editedTimelineRow->y(), 0, 0, 0, 0); + timelineContent->ensureVisible(TimelineConstants::TREE_BOUND_W + visiblePtX, + m_editedTimelineRow->y(), 0, 0, 0, 0); } else if (m_clickedTimelineControlType == TimelineControlType::EndHandle) { double time = m_ruler->distanceToTime(distance - TimelineConstants::RULER_EDGE_OFFSET); @@ -195,10 +236,9 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) m_editedTimelineRow->setEndX(distance); m_editedTimelineRow->showToolTip(QCursor::pos()); rowManager()->updateRulerDuration(p.x() > right); - m_widgetTimeline->viewTimelineContent()->ensureVisible( - TimelineConstants::TREE_BOUND_W - + m_editedTimelineRow->getEndX() + edgeMargin, - m_editedTimelineRow->y(), 0, 0, 0, 0); + timelineContent->ensureVisible(TimelineConstants::TREE_BOUND_W + + m_editedTimelineRow->getEndX() + edgeMargin, + m_editedTimelineRow->y(), 0, 0, 0, 0); } else if (m_clickedTimelineControlType == TimelineControlType::Duration) { double time = m_ruler->distanceToTime( distance - TimelineConstants::RULER_EDGE_OFFSET) @@ -215,7 +255,7 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) m_editedTimelineRow->moveDurationTo(distance); m_editedTimelineRow->showToolTip(QCursor::pos()); rowManager()->updateRulerDuration(p.x() > right); - m_widgetTimeline->viewTimelineContent()->ensureVisible( + timelineContent->ensureVisible( TimelineConstants::TREE_BOUND_W + visiblePtX, m_editedTimelineRow->y(), 0, 0, 0, 0); } @@ -458,18 +498,19 @@ void TimelineGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) m_rowManager->selectRow(m_editedTimelineRow->rowTree(), ctrlKeyDown); // click position in ruler space m_editedTimelineRow->startDurationMove(m_pressPos.x() - m_ruler->x()); - m_autoScrollTimelineTimer.start(); } else if (m_clickedTimelineControlType == TimelineControlType::StartHandle || m_clickedTimelineControlType == TimelineControlType::EndHandle) { - m_autoScrollTimelineTimer.start(); } + m_autoScrollTimelineTimer.start(); } } } else { m_keyframeManager->deselectAllKeyframes(); - if (m_pressPos.x() > m_ruler->x() && m_pressPos.y() > TimelineConstants::ROW_H) + if (m_pressPos.x() > m_ruler->x() && m_pressPos.y() > TimelineConstants::ROW_H) { m_selectionRect->start(m_pressPos); + m_autoScrollTimelineTimer.start(); + } } } @@ -488,11 +529,7 @@ void TimelineGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) bool shift = event->modifiers() & Qt::ShiftModifier; if (m_dragging) { - if (m_selectionRect->isActive()) { - // resizing keyframe selection rect - m_selectionRect->updateSize(event->scenePos()); - m_keyframeManager->selectKeyframesInRect(m_selectionRect->rect()); - } else if (m_startRowMoverOnNextDrag || m_rowMover->isActive()) { + if (m_startRowMoverOnNextDrag || m_rowMover->isActive()) { // moving rows vertically (reorder/reparent) if (m_startRowMoverOnNextDrag) { m_startRowMoverOnNextDrag = false; @@ -600,6 +637,8 @@ void TimelineGraphicsScene::resetMousePressParams() m_autoScrollTriggerTimer.stop(); m_timebarToolTip->hide(); m_pressPos = invalidPoint; + m_lastAutoScrollX = -1.0; + m_lastAutoScrollY = -1.0; } QLabel *TimelineGraphicsScene::timebarTooltip() @@ -733,6 +772,14 @@ void TimelineGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *even QGraphicsScene::mouseDoubleClickEvent(event); } +void TimelineGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) +{ + // Make sure drag states update on wheel scrolls done during drag + m_lastAutoScrollX = -1.0; + m_lastAutoScrollY = -1.0; + QGraphicsScene::wheelEvent(wheelEvent); +} + void TimelineGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) { // Eat left/right arrow keys on tree side unless some item (e.g. label) has focus @@ -745,6 +792,12 @@ void TimelineGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) } else if (keyEvent->key() == Qt::Key_Delete && !m_rowMover->isActive()) { g_StudioApp.DeleteSelectedObject(); // Despite the name, this deletes objects and keyframes } + // Make sure drag states update on keyboard scrolls done during drag + if (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Right + || keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) { + m_lastAutoScrollX = -1.0; + m_lastAutoScrollY = -1.0; + } QGraphicsScene::keyPressEvent(keyEvent); } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h index b8ef2174..ec878bf0 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h @@ -93,7 +93,7 @@ protected: void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; - + void wheelEvent(QGraphicsSceneWheelEvent *wheelEvent); void keyPressEvent(QKeyEvent *keyEvent) override; void keyReleaseEvent(QKeyEvent *keyEvent) override; @@ -143,7 +143,8 @@ private: TreeControlType m_clickedTreeControlType = TreeControlType::None; double m_pressPosInKeyframe; double m_treeWidth = TimelineConstants::TREE_DEFAULT_W; - double m_lastPlayHeadX = 0; + double m_lastAutoScrollX = -1.0; + double m_lastAutoScrollY = -1.0; TExpandMap m_expandMap; RowTree *m_releaseSelectRow = nullptr; bool m_autoScrollDownOn = false; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h index 6ca6c734..cfeed854 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h @@ -99,6 +99,7 @@ public: CPt GetPreferredSize() override; void SetSize(long inX, long inY) override; bool isFullReconstructPending() const { return m_fullReconstruct; } + NavigationBar *navigationBar() const { return m_navigationBar; } protected: // DataModel callbacks |