summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp')
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp
new file mode 100644
index 00000000..05d24fa6
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/TimelineToolbar.cpp
@@ -0,0 +1,456 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "TimelineToolbar.h"
+#include "StudioApp.h"
+#include "Core.h"
+#include "Doc.h"
+#include "Dispatch.h"
+#include "DataInputSelectView.h"
+#include "DataInputDlg.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "StudioPreferences.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentEditor.h"
+#include "DocumentEditorEnumerations.h"
+#include "Dialogs.h"
+
+#include <QtWidgets/qslider.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qtimer.h>
+#include <QtWidgets/qpushbutton.h>
+#include <QtWidgets/qshortcut.h>
+
+TimelineToolbar::TimelineToolbar() : QToolBar()
+{
+ setContentsMargins(0, 0, 0, 0);
+ setIconSize(QSize(16, 16));
+
+ // create icons
+ static const QIcon iconLayer = QIcon(":/images/Objects-Layer-Normal.png");
+ static const QIcon iconDelete = QIcon(":/images/Action-Trash-Normal.png");
+ static const QIcon iconFirst = QIcon(":/images/playback_tools_first.png");
+ static const QIcon iconLast = QIcon(":/images/playback_tools_last.png");
+ static const QIcon iconZoomIn = QIcon(":/images/zoom_in.png");
+ static const QIcon iconZoomOut = QIcon(":/images/zoom_out.png");
+ m_iconDiActive = QIcon(":/images/Objects-DataInput-Active.png");
+ m_iconDiInactive = QIcon(":/images/Objects-DataInput-Inactive.png");
+ m_iconStop = QIcon(":/images/playback_tools_stop.png");
+ m_iconPlay = QIcon(":/images/playback_tools_play.png");
+
+ // create actions
+ QString ctrlKey(QStringLiteral("Ctrl+"));
+ QString altKey(QStringLiteral("Alt+"));
+#ifdef Q_OS_MACOS
+ ctrlKey = "⌘";
+ altKey = "⌥";
+#endif
+ QString newLayerString = tr("Add New Layer (%1L)").arg(ctrlKey);
+ m_actionNewLayer = new QAction(iconLayer, newLayerString, this);
+ QAction *actionFirst = new QAction(iconFirst, tr("Go to Timeline Start"), this);
+ QAction *actionLast = new QAction(iconLast, tr("Go to Timeline End"), this);
+ m_actionDataInput = new QAction(m_iconDiInactive, tr("No Controller"), this);
+ m_actionDeleteRow = new QAction(iconDelete, tr("Delete Selected Object (Del)"), this);
+ m_actionPlayStop = new QAction(this);
+ m_timeLabel = new QPushButton(this);
+ m_diLabel = new QLabel(this);
+ m_actionZoomIn = new QAction(iconZoomIn, tr("Zoom In"), this);
+ m_actionZoomOut = new QAction(iconZoomOut, tr("Zoom Out"), this);
+
+ m_scaleSlider = new QSlider();
+ m_scaleSlider->setOrientation(Qt::Horizontal);
+ m_scaleSlider->setFixedWidth(100);
+ m_scaleSlider->setMinimum(1);
+ m_scaleSlider->setMaximum(22);
+ m_scaleSlider->setValue(2);
+
+ m_timeLabel->setObjectName(QLatin1String("timelineButton"));
+ m_timeLabel->setFlat(true);
+ m_timeLabel->setMinimumWidth(80);
+ m_timeLabel->setToolTip(tr("Go To Time (%1%2T)").arg(ctrlKey).arg(altKey));
+
+ m_diLabel->setText("");
+ m_diLabel->setMinimumWidth(100);
+ m_diLabel->setAlignment(Qt::AlignCenter);
+ QString styleString = "QLabel { background: transparent; color: "
+ + QString(CStudioPreferences::dataInputColor().name()) + "; }";
+ m_diLabel->setStyleSheet(styleString);
+
+ m_actionShowRowTexts = new QAction(tr("Toggle Timebars Text Visibility"), this);
+ QIcon rowTextIcon { QPixmap(":/images/timeline_text_hidden.png") };
+ rowTextIcon.addPixmap(QPixmap(":/images/timeline_text_shown.png"), QIcon::Normal, QIcon::On);
+ m_actionShowRowTexts->setIcon(rowTextIcon);
+ m_actionShowRowTexts->setCheckable(true);
+ m_actionFilter = new QAction(tr("Filter Timeline Rows Visibility According to Variants Filter"),
+ this);
+ m_actionFilter->setCheckable(true);
+ QIcon filterIcon { QPixmap(":/images/filter.png") };
+ filterIcon.addPixmap(QPixmap(":/images/filter-colored.png"), QIcon::Normal, QIcon::On);
+ m_actionFilter->setIcon(filterIcon);
+
+ updatePlayButtonState(false);
+
+ // connections
+ connect(m_actionNewLayer, &QAction::triggered, this, &TimelineToolbar::newLayerTriggered);
+ connect(m_actionDeleteRow, &QAction::triggered, this, &TimelineToolbar::deleteLayerTriggered);
+ connect(m_timeLabel, &QPushButton::clicked, this, &TimelineToolbar::gotoTimeTriggered);
+ connect(actionFirst, &QAction::triggered, this, &TimelineToolbar::firstFrameTriggered);
+ connect(m_actionPlayStop, &QAction::triggered, this, &TimelineToolbar::onPlayButtonClicked);
+ connect(actionLast, &QAction::triggered, this, &TimelineToolbar::lastFrameTriggered);
+ connect(m_scaleSlider, &QSlider::valueChanged, this, &TimelineToolbar::onZoomLevelChanged);
+ connect(m_actionZoomIn, &QAction::triggered, this, &TimelineToolbar::onZoomInButtonClicked);
+ connect(m_actionZoomOut, &QAction::triggered, this, &TimelineToolbar::onZoomOutButtonClicked);
+ connect(m_actionDataInput, &QAction::triggered, this, &TimelineToolbar::onDiButtonClicked);
+ connect(m_actionShowRowTexts, &QAction::toggled, this, &TimelineToolbar::showRowTextsToggled);
+ connect(m_actionFilter, &QAction::toggled, this, &TimelineToolbar::variantsFilterToggled);
+
+ // add actions
+ addAction(m_actionNewLayer);
+ addAction(m_actionDeleteRow);
+ addAction(m_actionDataInput);
+ addSpacing(2);
+ addAction(m_actionShowRowTexts);
+ addAction(m_actionFilter);
+ addWidget(m_diLabel);
+ addSpacing(20);
+ addWidget(m_timeLabel);
+ addSpacing(20);
+ addAction(actionFirst);
+ addAction(m_actionPlayStop);
+ addAction(actionLast);
+ addSpacing(30);
+ addAction(m_actionZoomOut);
+ addWidget(m_scaleSlider);
+ addAction(m_actionZoomIn);
+
+ // add keyboard shortcuts
+ m_actionZoomOut->setShortcut(Qt::Key_Minus);
+ m_actionZoomOut->setShortcutContext(Qt::ApplicationShortcut);
+ m_actionZoomIn->setShortcut(Qt::Key_Plus);
+ m_actionZoomIn->setShortcutContext(Qt::ApplicationShortcut);
+ m_actionNewLayer->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_L));
+ m_actionNewLayer->setShortcutContext(Qt::ApplicationShortcut);
+
+ QShortcut *gotoTimeShortcut = new QShortcut(this);
+ gotoTimeShortcut->setKey(QKeySequence(Qt::ControlModifier | Qt::AltModifier | Qt::Key_T));
+ gotoTimeShortcut->setContext(Qt::ApplicationShortcut);
+ connect(gotoTimeShortcut, &QShortcut::activated, this, &TimelineToolbar::gotoTimeTriggered);
+
+ m_connectSelectionChange = g_StudioApp.GetCore()->GetDispatch()->ConnectSelectionChange(
+ std::bind(&TimelineToolbar::onSelectionChange, this, std::placeholders::_1));
+
+ // make datainput indicator listen to selection dialog choice
+ const QVector<EDataType> acceptedTypes = { EDataType::DataTypeRangedNumber };
+ m_dataInputSelector = new DataInputSelectView(acceptedTypes, this);
+ g_StudioApp.GetCore()->GetDispatch()->AddDataModelListener(this);
+ connect(m_dataInputSelector, &DataInputSelectView::dataInputChanged,
+ this, &TimelineToolbar::onDataInputChange);
+}
+
+TimelineToolbar::~TimelineToolbar()
+{
+ delete m_dataInputSelector;
+}
+
+void TimelineToolbar::onSelectionChange(Q3DStudio::SSelectedValue inNewSelectable)
+{
+ qt3dsdm::TInstanceHandleList selectedInstances = inNewSelectable.GetSelectedInstances();
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ CClientDataModelBridge *theClientBridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ bool canDelete = false;
+ for (size_t idx = 0, end = selectedInstances.size(); idx < end; ++idx) {
+ if (theClientBridge->CanDelete(selectedInstances[idx])) {
+ canDelete = true;
+ break;
+ }
+ }
+
+ m_actionDeleteRow->setEnabled(canDelete);
+}
+
+// add a spacer widget
+void TimelineToolbar::addSpacing(int width)
+{
+ auto *widget = new QWidget;
+ widget->setStyleSheet("background:transparent;");
+ widget->setFixedWidth(width);
+ addWidget(widget);
+}
+
+void TimelineToolbar::setTime(long totalMillis)
+{
+ long mins = totalMillis % 3600000 / 60000;
+ long secs = totalMillis % 60000 / 1000;
+ long millis = totalMillis % 1000;
+
+ m_timeLabel->setText(QString::asprintf("%01d:%02d.%03d", mins, secs, millis));
+}
+
+QString TimelineToolbar::getCurrentController() const
+{
+ return m_currController;
+}
+
+QAction *TimelineToolbar::actionShowRowTexts() const
+{
+ return m_actionShowRowTexts;
+}
+
+void TimelineToolbar::setNewLayerEnabled(bool enable)
+{
+ m_actionNewLayer->setEnabled(enable);
+}
+
+void TimelineToolbar::updatePlayButtonState(bool started)
+{
+ if (started) {
+ m_actionPlayStop->setIcon(m_iconStop);
+ m_actionPlayStop->setText(tr("Stop Playing (Enter)"));
+ } else {
+ m_actionPlayStop->setIcon(m_iconPlay);
+ m_actionPlayStop->setText(tr("Start Playing (Enter)"));
+ }
+}
+
+void TimelineToolbar::onPlayButtonClicked()
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ if (doc->IsPlaying())
+ emit stopTriggered();
+ else
+ emit playTriggered();
+}
+
+void TimelineToolbar::onZoomLevelChanged(int scale)
+{
+ m_actionZoomIn->setEnabled(scale < m_scaleSlider->maximum());
+ m_actionZoomOut->setEnabled(scale > m_scaleSlider->minimum());
+
+ emit timelineScaleChanged(scale);
+}
+
+void TimelineToolbar::onZoomInButtonClicked()
+{
+ m_scaleSlider->setValue(m_scaleSlider->value() + 1);
+}
+
+void TimelineToolbar::onZoomOutButtonClicked()
+{
+ m_scaleSlider->setValue(m_scaleSlider->value() - 1);
+}
+
+void TimelineToolbar::onDiButtonClicked()
+{
+ QWidget *diButton = widgetForAction(m_actionDataInput);
+ if (diButton) {
+ QPoint chooserPos = diButton->pos() + QPoint(diButton->size().width(),
+ diButton->size().height());
+ showDataInputChooser(mapToGlobal(chooserPos));
+ }
+}
+
+bool TimelineToolbar::isVariantsFilterOn() const
+{
+ return m_actionFilter->isChecked();
+}
+
+// Update datainput button state according to this timecontext control state.
+void TimelineToolbar::updateDataInputStatus()
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ qt3dsdm::Qt3DSDMPropertyHandle ctrldProp;
+ qt3dsdm::Qt3DSDMInstanceHandle timeCtxRoot = doc->GetActiveRootInstance();
+ CClientDataModelBridge *theClientBridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ if (theClientBridge->GetObjectType(timeCtxRoot) == EStudioObjectType::OBJTYPE_SCENE) {
+ ctrldProp = theClientBridge->GetObjectDefinitions().m_Scene.m_ControlledProperty;
+ } else if (theClientBridge->GetObjectType(timeCtxRoot) ==
+ EStudioObjectType::OBJTYPE_COMPONENT) {
+ ctrldProp = theClientBridge->GetObjectDefinitions().m_Component.m_ControlledProperty;
+ } else {
+ Q_ASSERT(false);
+ }
+
+ qt3dsdm::SValue controlledPropertyVal;
+ doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue(
+ timeCtxRoot, ctrldProp, controlledPropertyVal);
+ auto existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal);
+
+ QString newController;
+ int timelineStrPos = existingCtrl.indexOf("@timeline");
+ if (timelineStrPos != -1) {
+ int ctrStrPos = existingCtrl.lastIndexOf("$", timelineStrPos - 2);
+ newController = existingCtrl.mid(ctrStrPos + 1, timelineStrPos - ctrStrPos - 2);
+ }
+ if (newController != m_currController) {
+ m_currController = newController;
+ // Toggle if we changed to a controlled time context, or if icon current state
+ // differs from the control state of current time context
+ if (!m_currController.isEmpty()) {
+ m_actionDataInput->setToolTip(
+ tr("Timeline Controller:\n%1").arg(m_currController));
+ m_actionDataInput->setIcon(m_iconDiActive);
+ updateTimelineTitleColor(true);
+ } else {
+ // TODO actually delete the entire property instead of setting it as empty string
+ m_actionDataInput->setIcon(m_iconDiInactive);
+ m_actionDataInput->setToolTip(tr("No Controller"));
+ updateTimelineTitleColor(false);
+ }
+ m_diLabel->setText(m_currController);
+ emit controllerChanged(m_currController);
+ if (m_dataInputSelector && m_dataInputSelector->isVisible())
+ m_dataInputSelector->setCurrentController(m_currController);
+ }
+}
+
+void TimelineToolbar::showDataInputChooser(const QPoint &point)
+{
+ QString currCtr = m_currController.size() ?
+ m_currController : m_dataInputSelector->getNoneString();
+ QVector<QPair<QString, int>> dataInputList;
+
+ for (auto &it : qAsConst(g_StudioApp.m_dataInputDialogItems))
+ dataInputList.append({it->name, it->type});
+
+ m_dataInputSelector->setData(dataInputList, currCtr);
+
+ CDialogs::showWidgetBrowser(this, m_dataInputSelector, point,
+ CDialogs::WidgetBrowserAlign::ToolButton);
+}
+
+void TimelineToolbar::onDataInputChange(int handle, int instance, const QString &dataInputName)
+{
+ Q_UNUSED(handle)
+ Q_UNUSED(instance)
+
+ if (dataInputName == m_currController)
+ return;
+
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ CClientDataModelBridge *bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ QString fullTimeControlStr;
+
+ if (dataInputName != m_dataInputSelector->getNoneString()) {
+ m_actionDataInput->setToolTip(tr("Timeline Controller:\n%1").arg(dataInputName));
+ fullTimeControlStr = "$" + dataInputName + " @timeline";
+ m_actionDataInput->setIcon(m_iconDiActive);
+ m_currController = dataInputName;
+ updateTimelineTitleColor(true);
+ } else {
+ m_actionDataInput->setToolTip(tr("No Controller"));
+ // TODO actually delete the entire property instead of setting it as empty string
+ m_actionDataInput->setIcon(m_iconDiInactive);
+ m_currController.clear();
+ updateTimelineTitleColor(false);
+ }
+
+ emit controllerChanged(m_currController);
+
+ // To indicate that this presentation timeline is controlled by data input,
+ // we set "controlled property" of this time context root (scene or component)
+ // to contain the name of controller followed by special indicator "@timeline".
+ // Either replace existing timeline control indicator string or append new one
+ // but do not touch @slide indicator string as scene can have both
+ qt3dsdm::Qt3DSDMPropertyHandle ctrldPropertyHandle;
+ qt3dsdm::Qt3DSDMInstanceHandle timeCtxRoot = doc->GetActiveRootInstance();
+ // Time context root is either scene or component
+ if (bridge->GetObjectType(timeCtxRoot) == EStudioObjectType::OBJTYPE_SCENE)
+ ctrldPropertyHandle = bridge->GetObjectDefinitions().m_Scene.m_ControlledProperty;
+ else if (bridge->GetObjectType(timeCtxRoot) == EStudioObjectType::OBJTYPE_COMPONENT)
+ ctrldPropertyHandle = bridge->GetObjectDefinitions().m_Component.m_ControlledProperty;
+ else
+ Q_ASSERT(false);
+
+ qt3dsdm::SValue controlledPropertyVal;
+ doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue(
+ timeCtxRoot, ctrldPropertyHandle, controlledPropertyVal);
+
+ auto existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal);
+ int slideStrPos = existingCtrl.indexOf("@timeline");
+ if (slideStrPos != -1) {
+ // find the controlling datainput name and build the string to replace
+ int ctrStrPos = existingCtrl.lastIndexOf("$", slideStrPos - 2);
+ QString prevCtrler = existingCtrl.mid(ctrStrPos, slideStrPos - ctrStrPos);
+ existingCtrl.replace(prevCtrler + "@timeline", fullTimeControlStr);
+ } else {
+ if (!existingCtrl.isEmpty() && m_currController.size())
+ existingCtrl.append(" ");
+ existingCtrl.append(fullTimeControlStr);
+ }
+
+ if (existingCtrl.endsWith(" "))
+ existingCtrl.chop(1);
+
+ if (existingCtrl.startsWith(" "))
+ existingCtrl.remove(0, 1);
+
+ m_diLabel->setText(m_currController);
+ qt3dsdm::SValue fullCtrlPropVal
+ = std::make_shared<qt3dsdm::CDataStr>(
+ Q3DStudio::CString::fromQString(existingCtrl));
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Timeline control"))
+ ->SetInstancePropertyValue(timeCtxRoot, ctrldPropertyHandle, fullCtrlPropVal);
+}
+
+void TimelineToolbar::OnBeginDataModelNotifications()
+{
+}
+
+void TimelineToolbar::OnEndDataModelNotifications()
+{
+ updateDataInputStatus();
+}
+
+void TimelineToolbar::OnImmediateRefreshInstanceSingle(qt3dsdm::Qt3DSDMInstanceHandle inInstance)
+{
+ Q_UNUSED(inInstance)
+}
+
+void TimelineToolbar::OnImmediateRefreshInstanceMultiple(qt3dsdm::Qt3DSDMInstanceHandle *inInstance,
+ long inInstanceCount)
+{
+ Q_UNUSED(inInstance)
+ Q_UNUSED(inInstanceCount)
+}
+
+// Notify the user about control state change also with timeline dock
+// title color change.
+void TimelineToolbar::updateTimelineTitleColor(bool controlled)
+{
+ QString styleString;
+ if (controlled) {
+ styleString = "QDockWidget#timeline { color: "
+ + QString(CStudioPreferences::dataInputColor().name()) + "; }";
+ } else {
+ styleString = "QDockWidget#timeline { color: "
+ + QString(CStudioPreferences::textColor().name()) + "; }";
+ }
+
+ QWidget *timelineDock = parentWidget()->parentWidget()->parentWidget();
+ timelineDock->setStyleSheet(styleString);
+}