Files
qt-creator/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp

490 lines
13 KiB
C++
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "transitioneditorgraphicsscene.h"
#include "transitioneditorgraphicslayout.h"
#include "transitioneditorpropertyitem.h"
#include "transitioneditorsectionitem.h"
#include "transitioneditortoolbar.h"
#include "transitioneditorview.h"
#include "transitioneditorwidget.h"
#include "timelineactions.h"
#include "timelineitem.h"
#include "timelinemovableabstractitem.h"
#include "timelinemovetool.h"
#include "timelineplaceholder.h"
#include "timelinepropertyitem.h"
#include "timelinesectionitem.h"
#include "timelineutils.h"
#include <designdocumentview.h>
#include <exception.h>
#include <rewritertransaction.h>
#include <rewriterview.h>
#include <viewmanager.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
#include <qmlobjectnode.h>
#include <qmltimelinekeyframegroup.h>
#include <bindingproperty.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <variantproperty.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
#include <QApplication>
#include <QComboBox>
#include <QGraphicsLinearLayout>
#include <QGraphicsProxyWidget>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QKeyEvent>
#include <cmath>
namespace QmlDesigner {
TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWidget *parent)
: AbstractScrollGraphicsScene(parent)
, m_parent(parent)
, m_layout(new TransitionEditorGraphicsLayout(this))
, m_tools(this)
{
addItem(m_layout);
setSceneRect(m_layout->geometry());
connect(m_layout, &QGraphicsWidget::geometryChanged, this, [this]() {
auto rect = m_layout->geometry();
setSceneRect(rect);
if (auto *gview = graphicsView())
gview->setSceneRect(rect.adjusted(0, TimelineConstants::rulerHeight, 0, 0));
if (auto *rview = rulerView())
rview->setSceneRect(rect);
});
auto changeScale = [this](int factor) {
transitionEditorWidget()->changeScaleFactor(factor);
setZoom(factor);
};
connect(m_layout, &TransitionEditorGraphicsLayout::zoomChanged, changeScale);
}
TransitionEditorGraphicsScene::~TransitionEditorGraphicsScene()
{
QSignalBlocker block(this);
qDeleteAll(items());
}
void TransitionEditorGraphicsScene::invalidateScrollbar()
{
double max = m_layout->maximumScrollValue();
transitionEditorWidget()->setupScrollbar(0, max, scrollOffset());
if (scrollOffset() > max)
setScrollOffset(max);
}
void TransitionEditorGraphicsScene::onShow()
{
emit m_layout->zoomChanged(zoom());
}
void TransitionEditorGraphicsScene::setTransition(const ModelNode &transition)
{
clearSelection();
m_layout->setTransition(transition);
}
void TransitionEditorGraphicsScene::clearTransition()
{
m_transition = {};
m_layout->setTransition({});
}
void TransitionEditorGraphicsScene::setWidth(int width)
{
m_layout->setWidth(width);
invalidateScrollbar();
}
void TransitionEditorGraphicsScene::invalidateLayout()
{
m_layout->invalidate();
}
void TransitionEditorGraphicsScene::setDuration(int duration)
{
if (m_transition.isValid())
m_transition.setAuxiliaryData("transitionDuration", duration);
m_layout->setDuration(duration);
qreal scaling = m_layout->rulerScaling();
setZoom(scaling);
}
int TransitionEditorGraphicsScene::zoom() const
{
return m_layout->zoom();
}
qreal TransitionEditorGraphicsScene::rulerScaling() const
{
return m_layout->rulerScaling();
}
int TransitionEditorGraphicsScene::rulerWidth() const
{
return m_layout->rulerWidth();
}
qreal TransitionEditorGraphicsScene::rulerDuration() const
{
return m_layout->rulerDuration();
}
qreal TransitionEditorGraphicsScene::endFrame() const
{
return m_layout->endFrame();
}
qreal TransitionEditorGraphicsScene::startFrame() const
{
return 0;
}
qreal TransitionEditorGraphicsScene::mapToScene(qreal x) const
{
return TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset
+ (x - startFrame()) * rulerScaling() - scrollOffset();
}
qreal TransitionEditorGraphicsScene::mapFromScene(qreal x) const
{
auto xPosOffset = (x - TimelineConstants::sectionWidth - TimelineConstants::timelineLeftOffset)
+ scrollOffset();
return xPosOffset / rulerScaling() + startFrame();
}
void TransitionEditorGraphicsScene::setZoom(int scaleFactor)
{
m_layout->setZoom(scaleFactor);
setScrollOffset(0);
invalidateSections();
invalidateScrollbar();
update();
}
void TransitionEditorGraphicsScene::setZoom(int scaling, double pivot)
{
const qreal oldOffset = scrollOffset();
const qreal oldScaling = m_layout->rulerScaling();
const qreal oldPosition = mapToScene(pivot);
m_layout->setZoom(scaling);
const qreal newScaling = m_layout->rulerScaling();
const qreal newPosition = mapToScene(pivot);
const qreal newOffset = oldOffset + (newPosition - oldPosition);
if (std::isinf(oldScaling) || std::isinf(newScaling))
setScrollOffset(0);
else {
setScrollOffset(std::round(newOffset));
const qreal start = mapToScene(startFrame());
const qreal head = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset;
if (start - head > 0)
setScrollOffset(0);
}
invalidateSections();
invalidateScrollbar();
update();
}
void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &target)
{
if (!target.isValid())
return;
bool found = false;
const QList<QGraphicsItem *> items = m_layout->childItems();
for (auto child : items)
TransitionEditorSectionItem::updateDataForTarget(child, target, &found);
if (!found)
invalidateScene();
clearSelection();
invalidateLayout();
}
void TransitionEditorGraphicsScene::invalidateHeightForTarget(const ModelNode &target)
{
if (!target.isValid())
return;
const auto children = m_layout->childItems();
for (auto child : children)
TransitionEditorSectionItem::updateHeightForTarget(child, target);
invalidateLayout();
}
void TransitionEditorGraphicsScene::invalidateScene()
{
invalidateScrollbar();
}
void TransitionEditorGraphicsScene::invalidateCurrentValues()
{
const QList<QGraphicsItem *> constItems = items();
for (auto item : constItems)
TimelinePropertyItem::updateTextEdit(item);
}
QGraphicsView *TransitionEditorGraphicsScene::graphicsView() const
{
const QList<QGraphicsView *> constViews = views();
for (auto *v : constViews)
if (v->objectName() == "SceneView")
return v;
return nullptr;
}
QGraphicsView *TransitionEditorGraphicsScene::rulerView() const
{
const QList<QGraphicsView *> constViews = views();
for (auto *v : constViews)
if (v->objectName() == "RulerView")
return v;
return nullptr;
}
QRectF TransitionEditorGraphicsScene::selectionBounds() const
{
QRectF bbox;
return bbox;
}
void TransitionEditorGraphicsScene::clearSelection()
{
if (m_selectedProperty)
m_selectedProperty->update();
m_selectedProperty = nullptr;
AbstractScrollGraphicsScene::clearSelection();
}
QList<QGraphicsItem *> TransitionEditorGraphicsScene::itemsAt(const QPointF &pos)
{
QTransform transform;
if (auto *gview = graphicsView())
transform = gview->transform();
return items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder, transform);
}
void TransitionEditorGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mousePressEvent(topItem, event);
QGraphicsScene::mousePressEvent(event);
}
void TransitionEditorGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mouseMoveEvent(topItem, event);
QGraphicsScene::mouseMoveEvent(event);
}
void TransitionEditorGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
/* The tool has handle the event last. */
QGraphicsScene::mouseReleaseEvent(event);
m_tools.mouseReleaseEvent(topItem, event);
}
void TransitionEditorGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
auto topItem = TimelineMovableAbstractItem::topMoveableItem(itemsAt(event->scenePos()));
m_tools.mouseDoubleClickEvent(topItem, event);
QGraphicsScene::mouseDoubleClickEvent(event);
}
void TransitionEditorGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
{
if (qgraphicsitem_cast<QGraphicsProxyWidget *>(focusItem())) {
keyEvent->ignore();
QGraphicsScene::keyPressEvent(keyEvent);
return;
}
if (keyEvent->modifiers().testFlag(Qt::ControlModifier)) {
QGraphicsScene::keyPressEvent(keyEvent);
} else {
switch (keyEvent->key()) {
case Qt::Key_Left:
emit scroll(TimelineUtils::Side::Left);
keyEvent->accept();
break;
case Qt::Key_Right:
emit scroll(TimelineUtils::Side::Right);
keyEvent->accept();
break;
default:
QGraphicsScene::keyPressEvent(keyEvent);
break;
}
}
}
void TransitionEditorGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
{
if (qgraphicsitem_cast<QGraphicsProxyWidget *>(focusItem())) {
keyEvent->ignore();
QGraphicsScene::keyReleaseEvent(keyEvent);
return;
}
QGraphicsScene::keyReleaseEvent(keyEvent);
}
void TransitionEditorGraphicsScene::focusOutEvent(QFocusEvent *focusEvent)
{
QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_TRANSITIONEDITOR_TIME,
m_usageTimer.elapsed());
QGraphicsScene::focusOutEvent(focusEvent);
}
void TransitionEditorGraphicsScene::focusInEvent(QFocusEvent *focusEvent)
{
m_usageTimer.restart();
QGraphicsScene::focusInEvent(focusEvent);
}
void TransitionEditorGraphicsScene::invalidateSections()
{
const QList<QGraphicsItem *> children = m_layout->childItems();
for (auto child : children)
TransitionEditorSectionItem::updateData(child);
clearSelection();
invalidateLayout();
}
QmlDesigner: Add animation playback for timeline Adds animation playback, playback speed and looping inputs in the toolbar -Animation can be seen in the form editor in real time -Play button toggles the animation playback -Playback speed can be manually set -Looping can be toggled on and off -Looping range can be manually set Looping range can be created and edited in multiple ways. These shortcuts are currently placeholders and should be changed if needed. -LeftMouseButton+CTRL will set the loop start point and dragging the mouse will let you paint the range. -Hovering over the looping start or end point then dragging with LeftMouseButton+CTRL down will allow individual point movement -Pressing SHIFT will enable toolbar snapping when painting/moving looping points. will allow individual movement by dragging it with LeftMouseButton -Clicking a component in the timeline with LeftMouseButton+CTRL will set the components start and end time as the loop range. Using LeftMouseButton+CTRL+SHIFT instead will extend the current loop range to include the clicked component. Keyframes work the same way. -Using the selection tool by dragging the timeline with LeftMouseButton+CTRL will set the loop range to contain all the selected components. Using LeftMouseButton+CTRL+SHIFT instead will extend the current loop range to include the animations start, mid or end time, depending on which overlap with the selection tool rectangle. Keyframes work the same way. Task-number: QDS-1335 Change-Id: I8a623bae6ab43b41f894f2a33a1a597a48f82c68 Reviewed-by: Knud Dollereder <knud.dollereder@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2021-08-10 16:19:43 +03:00
TimelineRulerSectionItem *TransitionEditorGraphicsScene::layoutRuler() const
{
return m_layout->ruler();
}
TransitionEditorView *TransitionEditorGraphicsScene::transitionEditorView() const
{
return m_parent->transitionEditorView();
}
TransitionEditorWidget *TransitionEditorGraphicsScene::transitionEditorWidget() const
{
return m_parent;
}
TransitionEditorToolBar *TransitionEditorGraphicsScene::toolBar() const
{
return transitionEditorWidget()->toolBar();
}
void TransitionEditorGraphicsScene::activateLayout()
{
m_layout->activate();
}
AbstractView *TransitionEditorGraphicsScene::abstractView() const
{
return transitionEditorView();
}
bool TransitionEditorGraphicsScene::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ShortcutOverride:
if (TimelineUtils::isDeleteKey(static_cast<QKeyEvent *>(event)->key())) {
QGraphicsScene::keyPressEvent(static_cast<QKeyEvent *>(event));
event->accept();
return true;
}
Q_FALLTHROUGH();
default:
return QGraphicsScene::event(event);
}
}
ModelNode TransitionEditorGraphicsScene::transitionModelNode() const
{
if (transitionEditorView()->isAttached()) {
const QString timelineId = transitionEditorWidget()->toolBar()->currentTransitionId();
return transitionEditorView()->modelNodeForId(timelineId);
}
return ModelNode();
}
TransitionEditorPropertyItem *TransitionEditorGraphicsScene::selectedPropertyItem() const
{
return m_selectedProperty;
}
void TransitionEditorGraphicsScene::setSelectedPropertyItem(TransitionEditorPropertyItem *item)
{
if (m_selectedProperty)
m_selectedProperty->update();
m_selectedProperty = item;
emit selectionChanged();
}
} // namespace QmlDesigner