diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/Action')
31 files changed, 4931 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.cpp new file mode 100644 index 00000000..1e194d79 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2005 NVIDIA Corporation. +** Copyright (C) 2017 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 "Qt3DSCommonPrecompile.h" + +#include "ActionContextMenu.h" +#include "StudioClipboard.h" + +CActionContextMenu::CActionContextMenu(QList<QAction *> actions, QWidget *parent) + : QMenu(parent) +{ + addActions(actions); +} + +CActionContextMenu::~CActionContextMenu() +{ +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.h b/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.h new file mode 100644 index 00000000..99d785aa --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionContextMenu.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2005 NVIDIA Corporation. +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef INCLUDED_ACTION_CONTEXT_MENU_H +#define INCLUDED_ACTION_CONTEXT_MENU_H 1 + +#include <QtWidgets/qmenu.h> + +class CActionContextMenu : public QMenu +{ + Q_OBJECT +public: + explicit CActionContextMenu(QList<QAction *> actions, QWidget *parent = nullptr); + virtual ~CActionContextMenu(); +}; +#endif // INCLUDED_ACTION_CONTEXT_MENU_H diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.cpp new file mode 100644 index 00000000..556215de --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "ActionModel.h" + +#include "Qt3DSCommonPrecompile.h" +#include "ClientDataModelBridge.h" +#include "CmdDataModelActionSetValue.h" +#include "Core.h" +#include "Doc.h" +#include "StudioApp.h" +#include "Qt3DSDMActionSystem.h" +#include "Qt3DSDMStudioSystem.h" + +ActionModel::ActionModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +void ActionModel::setInstanceHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle) +{ + beginResetModel(); + m_handle = handle; + auto doc = g_StudioApp.GetCore()->GetDoc(); + m_actions.clear(); + if (handle.Valid()) { + doc->GetStudioSystem()->GetActionSystem()->GetActions(doc->GetActiveSlide(), + handle, m_actions); + } + + endResetModel(); +} + +QHash<int, QByteArray> ActionModel::roleNames() const +{ + auto names = QAbstractItemModel::roleNames(); + names.insert(DescriptionRole, "description"); + names.insert(VisibleRole, "visible"); + + return names; +} + + +int ActionModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return int(m_actions.size()); +} + +QVariant ActionModel::data(const QModelIndex &index, int role) const +{ + if (!hasIndex(index.row(), index.column(), index.parent())) + return {}; + + const auto action = m_actions.at(index.row()); + auto system = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem(); + if (action.Valid()) { + auto actionCore = system->GetActionCore(); + + // Ensure the handle is still valid on the back-end, as some undo scenarios may cause this + // function to be called for already deleted actions + if (actionCore->HandleValid(action)) { + switch (role) + { + case DescriptionRole: + return actionString(action); + case VisibleRole: + return system->GetActionSystem()->GetActionEyeballValue(activeSlide(), action); + default: + return {}; + } + } + } + return {}; +} + +bool ActionModel::setData(const QModelIndex &index, const QVariant &data, int role) +{ + if (!hasIndex(index.row(), index.column(), index.parent())) + return false; + + const auto action = m_actions.at(index.row()); + + if (role == VisibleRole) { + auto doc = g_StudioApp.GetCore()->GetDoc(); + CCmd *theCmd = new CCmdDataModelActionSetEyeball(doc, activeSlide(), action, data.toBool()); + g_StudioApp.GetCore()->ExecuteCommand(theCmd); + Q_EMIT dataChanged(index, index, {VisibleRole}); + } + + + return false; +} + +void ActionModel::addAction(const Qt3DSDMActionHandle &action) +{ + if (std::find(m_actions.begin(), m_actions.end(), action) == m_actions.end()) { + const auto count = rowCount(); + beginInsertRows({}, count, count); + m_actions.push_back(action); + endInsertRows(); + } +} + +void ActionModel::removeAction(const Qt3DSDMActionHandle &action) +{ + // KDAB_FIXME use beginRemoveRows + beginResetModel(); + m_actions.erase(std::remove(m_actions.begin(), m_actions.end(), action), m_actions.end()); + endResetModel(); +} + +void ActionModel::updateAction(const Qt3DSDMActionHandle &action) +{ + for (unsigned i = 0; i < m_actions.size(); i++) { + if (m_actions[i] == action) { + auto idx = index(i, 0); + Q_EMIT dataChanged(idx, idx, {}); + } + } +} + +const Qt3DSDMActionHandle ActionModel::actionAt(int row) +{ + if (row >= 0 && static_cast<unsigned>(row) < m_actions.size()) + return m_actions[row]; + + return {}; +} + +const SActionInfo ActionModel::actionInfoAt(int row) +{ + const auto action = actionAt(row); + if (!action.Valid()) + return {}; + auto actionCore = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetActionCore(); + return actionCore->GetActionInfo(action); +} + +qt3dsdm::IActionSystem *ActionModel::actionSystem() const +{ + return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetActionSystem(); +} + +qt3dsdm::Qt3DSDMSlideHandle ActionModel::activeSlide() const +{ + return g_StudioApp.GetCore()->GetDoc()->GetActiveSlide(); +} + +QString ActionModel::actionString(const Qt3DSDMActionHandle &action) const +{ + QString result; + if (action.Valid()) { + auto core = g_StudioApp.GetCore(); + auto doc = core->GetDoc(); + auto actionCore = doc->GetStudioSystem()->GetActionCore(); + auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + + const SActionInfo &actionInfo = actionCore->GetActionInfo(action); + + // Query the event name + QString eventFormalName(tr("[Unknown Event]")); + Qt3DSDMEventHandle eventHandle = bridge->ResolveEvent(actionInfo); + if (eventHandle.Valid()) + eventFormalName = + QString::fromWCharArray(bridge->GetEventInfo(eventHandle).m_FormalName.wide_str()); + + // Query the asset name + QString assetName = tr("[Unknown]"); + assetName = bridge->GetName(actionInfo.m_Owner).toQString(); + + const auto sourceInstance = + bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject); + const auto targetInstance = + bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject); + QString sourceName = sourceInstance.Valid() + ? bridge->GetName(sourceInstance).toQString() + : tr("[Unknown Source]"); + QString targetName = targetInstance.Valid() + ? bridge->GetName(targetInstance).toQString() + : tr("[Unknown Target]"); + + // Query the action name + QString handlerFormalName(tr("[Unknown Handler]")); + const auto handlerHandle = bridge->ResolveHandler(actionInfo); + if (handlerHandle.Valid()) + handlerFormalName = QString::fromWCharArray(bridge->GetHandlerInfo(handlerHandle).m_FormalName.wide_str()); + + // Format the strings + if (actionInfo.m_Owner == sourceInstance) { + if (sourceInstance == targetInstance) { + result = tr("Listen for '%1', '%2'").arg(eventFormalName, handlerFormalName); + } else { + result = tr("Listen for '%1', tell %2 to '%3'").arg(eventFormalName, targetName, + handlerFormalName); + } + } else if (actionInfo.m_Owner == targetInstance) { + result = tr("Listen to '%1' for '%2', '%3'").arg(sourceName, eventFormalName, + handlerFormalName); + } else { + result = tr("Listen to '%1' for '%2', tell %3 to '%4'").arg(sourceName, + eventFormalName, + targetName, + handlerFormalName); + } + } + return result; +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.h b/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.h new file mode 100644 index 00000000..966c5782 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionModel.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef ACTIONMODEL_H +#define ACTIONMODEL_H + +#include <QAbstractListModel> + +#include "Qt3DSDMActionInfo.h" +#include "Qt3DSDMHandles.h" + +namespace qt3dsdm { + class IActionSystem; +} + +class ActionModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit ActionModel(QObject *parent = nullptr); + + void setInstanceHandle(const qt3dsdm::Qt3DSDMInstanceHandle &handle); + + enum Roles { + DescriptionRole = Qt::DisplayRole, + VisibleRole = Qt::UserRole + 1, + + }; + + QHash<int, QByteArray> roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override; + + void addAction(const qt3dsdm::Qt3DSDMActionHandle &action); + void removeAction(const qt3dsdm::Qt3DSDMActionHandle &action); + void updateAction(const qt3dsdm::Qt3DSDMActionHandle &action); + const qt3dsdm::Qt3DSDMActionHandle actionAt(int row); + const qt3dsdm::SActionInfo actionInfoAt(int row); + +private: + qt3dsdm::IActionSystem *actionSystem() const; + qt3dsdm::Qt3DSDMSlideHandle activeSlide() const; + QString actionString(const qt3dsdm::Qt3DSDMActionHandle &action) const; + + qt3dsdm::Qt3DSDMInstanceHandle m_handle; + qt3dsdm::TActionHandleList m_actions; +}; + +#endif // ACTIONMODEL_H diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.cpp new file mode 100644 index 00000000..41d67789 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.cpp @@ -0,0 +1,1203 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "ActionView.h" +#include "ActionContextMenu.h" +#include "ActionModel.h" +#include "CmdDataModelActionSetValue.h" +#include "ClientDataModelBridge.h" +#include "Core.h" +#include "Dialogs.h" +#include "Dispatch.h" +#include "Doc.h" +#include "IDocumentEditor.h" +#include "IDocumentReader.h" +#include "IObjectReferenceHelper.h" +#include "Literals.h" +#include "ObjectListModel.h" +#include "StudioUtils.h" +#include "StudioApp.h" +#include "StudioClipboard.h" +#include "StudioObjectTypes.h" +#include "StudioPreferences.h" +#include "Qt3DSFileTools.h" +#include "Qt3DSDMActionCore.h" +#include "Qt3DSDMDataTypes.h" +#include "Qt3DSDMSlides.h" + +#include <QtCore/qcoreapplication.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> +#include <QtCore/qtimer.h> +#include <QtWidgets/qdesktopwidget.h> + +ActionView::ActionView(const QSize &preferredSize, QWidget *parent) + : QQuickWidget(parent) + , TabNavigable() + , m_actionsModel(new ActionModel(this)) + , m_preferredSize(preferredSize) +{ + setResizeMode(QQuickWidget::SizeRootObjectToView); + QTimer::singleShot(0, this, &ActionView::initialize); + + g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this); + + connect(this, &ActionView::actionChanged, this, [this] { + if (!m_itemHandle.Valid()) + return; + + if (!m_propertyModel) + m_propertyModel = new PropertyModel(this); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + if (actionInfo.m_Handler == L"Set Property") { + setPropertyValueInvalid(true); + m_currentPropertyNameHandle = actionInfo.m_HandlerArgs.at(0); + m_currentPropertyValueHandle = actionInfo.m_HandlerArgs.at(1); + m_propertyModel->setAction(m_actionsModel->actionAt(m_currentActionIndex)); + m_propertyModel->setNameHandle(m_currentPropertyNameHandle); + m_propertyModel->setValueHandle(m_currentPropertyValueHandle); + m_currentPropertyIndex = m_propertyModel->defaultPropertyIndex(); + Q_EMIT propertyChanged(); + Q_EMIT propertyModelChanged(); + setPropertyValueInvalid(false); + } + }); + + m_actionChangedCompressionTimer.setInterval(20); + m_actionChangedCompressionTimer.setSingleShot(true); + connect(&m_actionChangedCompressionTimer, &QTimer::timeout, this, [this] { + updateHandlerArguments(); + updateFiredEvent(); + Q_EMIT actionChanged(); + }); + + QString ctrlKey(QStringLiteral("Ctrl+")); + QString shiftKey(QStringLiteral("Shift+")); +#ifdef Q_OS_MACOS + ctrlKey = "⌘"; + shiftKey = "⇧"; +#endif + + // These actions will be passed to the context menu. Some of them need to me members, as we + // have to change their enabled state based on selection and previous actions. + QAction *action = new QAction(tr("New Action\t%1A").arg(shiftKey)); + action->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_A)); + connect(action, &QAction::triggered, this, &ActionView::addAction); + QQuickWidget::addAction(action); + + m_actionCopy = new QAction(tr("Copy Action\t%1C").arg(ctrlKey)); + connect(m_actionCopy, &QAction::triggered, this, &ActionView::copyAction); + QQuickWidget::addAction(m_actionCopy); + + m_actionPaste = new QAction(tr("Paste Action\t%1V").arg(ctrlKey)); + connect(m_actionPaste, &QAction::triggered, this, &ActionView::pasteAction); + QQuickWidget::addAction(m_actionPaste); + + m_actionCut = new QAction(tr("Cut Action\t%1X").arg(ctrlKey)); + connect(m_actionCut, &QAction::triggered, this, &ActionView::cutAction); + QQuickWidget::addAction(m_actionCut); + + m_actionDel = new QAction(tr("Delete Action\tDel")); + connect(m_actionDel, &QAction::triggered, [=](){ deleteAction(m_currentActionIndex); }); + QQuickWidget::addAction(m_actionDel); +} + +ActionView::~ActionView() +{ +} + +QSize ActionView::sizeHint() const +{ + return m_preferredSize; +} + +void ActionView::focusInEvent(QFocusEvent *event) +{ + updateActionStates(); + QQuickWidget::focusInEvent(event); +} + +void ActionView::mousePressEvent(QMouseEvent *event) +{ + g_StudioApp.setLastActiveView(this); + QQuickWidget::mousePressEvent(event); +} + +bool ActionView::event(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast<QKeyEvent *>(event); + if (m_currentActionIndex >= 0 && (ke->key() == Qt::Key_Delete + || (ke->modifiers() == Qt::ControlModifier + && (ke->key() == Qt::Key_C || ke->key() == Qt::Key_V + || ke->key() == Qt::Key_X)))) { + auto focusItem = quickWindow()->activeFocusItem(); + if (focusItem && (focusItem->objectName() == QStringLiteral("actionListDelegate") + || focusItem->objectName() == QStringLiteral("focusEater"))) { + if (ke->key() == Qt::Key_Delete) { + if (m_actionDel->isEnabled()) + deleteAction(m_currentActionIndex); + } else if (ke->modifiers() == Qt::ControlModifier) { + if (ke->key() == Qt::Key_C) { + if (m_actionCopy->isEnabled()) + copyAction(); + } else if (ke->key() == Qt::Key_V) { + if (m_actionPaste->isEnabled()) + pasteAction(); + } else if (ke->key() == Qt::Key_X) { + if (m_actionCut->isEnabled()) + cutAction(); + } + } + event->accept(); + return true; + } + } + } + return QQuickWidget::event(event); +} + +void ActionView::setItem(const qt3dsdm::Qt3DSDMInstanceHandle &handle) +{ + if (!m_activeBrowser.isNull() && m_activeBrowser->isVisible()) { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } + + m_objRefHelper = GetDoc()->GetDataModelObjectReferenceHelper(); + m_itemHandle = handle; + m_actionsModel->setInstanceHandle(handle); + if (m_itemHandle.Valid() != m_hasItem) { + m_hasItem = m_itemHandle.Valid(); + Q_EMIT hasItemChanged(); + } + emitActionChanged(); + Q_EMIT itemChanged(); + Q_EMIT itemTextChanged(); +} + +QString ActionView::itemIcon() const +{ + if (!m_itemHandle.Valid()) + return QString(); + + auto info = m_objRefHelper->GetInfo(m_itemHandle); + return CStudioObjectTypes::GetNormalIconName(info.m_Type); +} + +QString ActionView::itemText() const +{ + if (!m_itemHandle.Valid()) + return tr("No Object Selected"); + + const auto data = m_objRefHelper->GetInfo(m_itemHandle); + return data.m_Name.toQString(); +} + +QColor ActionView::itemColor() const +{ + if (!m_itemHandle.Valid()) + return Qt::white; + + auto info = m_objRefHelper->GetInfo(m_itemHandle); + if (info.m_Master) + return CStudioPreferences::masterColor(); + else + return CStudioPreferences::textColor(); +} + +QAbstractItemModel *ActionView::actionsModel() const +{ + return m_actionsModel; +} + +QAbstractItemModel *ActionView::propertyModel() const +{ + return m_propertyModel; +} + +QString ActionView::targetObjectName() const +{ + if (!GetDoc()->isValid() || !m_itemHandle.Valid()) + return QString(); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + + const auto targetInstance = + GetBridge()->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject); + + QString targetName = targetInstance.Valid() + ? GetBridge()->GetName(targetInstance).toQString() + : tr("[Unknown Target]"); + + return targetName; +} + +QString ActionView::triggerObjectName() const +{ + if (!GetDoc()->isValid() || !m_itemHandle.Valid()) + return QString(); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + + const auto sourceInstance = + GetBridge()->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject); + + QString sourceName = sourceInstance.Valid() + ? GetBridge()->GetName(sourceInstance).toQString() + : tr("[Unknown Source]"); + + return sourceName; +} + +QString ActionView::eventName() const +{ + if (!GetDoc()->isValid() || !m_itemHandle.Valid()) + return QString(); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto eventHandle = bridge->ResolveEvent(actionInfo); + const auto eventInfo = bridge->GetEventInfo(eventHandle); + + const QString formalName = QString::fromWCharArray(eventInfo.m_FormalName.wide_str()); + return formalName.isEmpty() ? tr("[Unknown Event]") : formalName; +} + +QString ActionView::handlerName() const +{ + if (!GetDoc()->isValid() || !m_itemHandle.Valid()) + return QString(); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto handlerHandle = bridge->ResolveHandler(actionInfo); + + if (handlerHandle.Valid()) { + const auto handlerInfo = bridge->GetHandlerInfo(handlerHandle); + return QString::fromWCharArray(handlerInfo.m_FormalName.wide_str()); + } + + return tr("[Unknown Handler]"); +} + +QVariantList ActionView::handlerArguments() const +{ + return m_handlerArguments; +} + +PropertyInfo ActionView::property() const +{ + if (!m_propertyModel) + return {}; + return m_propertyModel->property(m_currentPropertyIndex); +} + +bool ActionView::isPropertyValueInvalid() const +{ + return m_propertyValueInvalid; +} + +void ActionView::setCurrentActionIndex(int index) +{ + if (index == m_currentActionIndex) + return; + + m_currentActionIndex = index; + emitActionChanged(); + + updateActionStates(); +} + +void ActionView::setCurrentPropertyIndex(int handle, int index) +{ + setPropertyValueInvalid(true); + // Make sure propertymodel name & value handles are always up-to-date, + // even when index is same as before + m_currentPropertyValueHandle = 0; + m_currentPropertyNameHandle = handle; + for (int i = 0; i < m_handlerArguments.size(); ++i) { + auto handlerArg = m_handlerArguments[i].value<HandlerArgument>(); + if (handlerArg.m_handle.GetHandleValue() == handle && i < m_handlerArguments.size() - 1) { + m_currentPropertyValueHandle + = m_handlerArguments[i + 1].value<HandlerArgument>().m_handle; + if (m_propertyModel) { + m_propertyModel->setNameHandle(m_currentPropertyNameHandle); + m_propertyModel->setValueHandle(m_currentPropertyValueHandle); + } + } + } + + if (index == m_currentPropertyIndex) + return; + + m_currentPropertyIndex = index; + + // set the property for the handler + if (m_propertyModel && handle != 0) { + qt3dsdm::SValue sValue(QVariant(m_propertyModel->property(index).m_nameId)); + qt3dsdm::SValue oldValue; + GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, oldValue); + + if (!Equals(oldValue, sValue)) { + CCmd *theCmd = + new CCmdDataModelActionSetArgumentValue(GetDoc(), handle, sValue); + g_StudioApp.GetCore()->ExecuteCommand(theCmd); + } + } + + Q_EMIT propertyChanged(); + // Clear the value invalid flag asynchronously as the value doesn't actually change until + // backend tells us it does + QTimer::singleShot(0, this, &ActionView::clearPropertyValueInvalid); +} + +void ActionView::addAction() +{ + if (m_itemHandle.Valid()) { + // Query data model bridge to see the applicable events and actions for this instance. + CClientDataModelBridge *theBridge = GetBridge(); + + std::wstring theEventName = theBridge->GetDefaultEvent(m_itemHandle); + std::wstring theHandlerName = theBridge->GetDefaultHandler(m_itemHandle); + + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Add Action")) + ->AddAction(GetDoc()->GetActiveSlide(), m_itemHandle, theEventName, + theHandlerName); + } + updateActionStates(); +} + +void ActionView::deleteAction(int index) +{ + if (!m_itemHandle.Valid()) + return; + + const auto action = m_actionsModel->actionAt(index); + if (action.Valid()) { + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), + QObject::tr("Delete Action"))->DeleteAction(action); + emitActionChanged(); + } + updateActionStates(); +} + +QObject *ActionView::showTriggerObjectBrowser(const QPoint &point) +{ + if (!m_itemHandle.Valid()) + return nullptr; + + if (!m_objectsModel) { + m_objectsModel = new ObjectListModel(g_StudioApp.GetCore(), + GetDoc()->GetSceneInstance(), this); + } + + if (!m_triggerObjectBrowser) + m_triggerObjectBrowser = new ObjectBrowserView(this); + + m_triggerObjectBrowser->setModel(m_objectsModel); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto instanceHandle = GetBridge()->GetInstance(actionInfo.m_Owner, + actionInfo.m_TriggerObject); + m_triggerObjectBrowser->disconnect(); + m_triggerObjectBrowser->selectAndExpand(instanceHandle, actionInfo.m_Owner); + CDialogs::showWidgetBrowser(this, m_triggerObjectBrowser, point); + m_activeBrowser = m_triggerObjectBrowser; + + connect(m_triggerObjectBrowser, &ObjectBrowserView::selectionChanged, + this, &ActionView::OnTriggerSelectionChanged); + // update also pathtype in the trigger object when changed from UI + connect(m_triggerObjectBrowser, &ObjectBrowserView::pathTypeChanged, + this, &ActionView::OnTriggerSelectionChanged); + + return m_triggerObjectBrowser; +} + +QObject *ActionView::showTargetObjectBrowser(const QPoint &point) +{ + if (!m_itemHandle.Valid()) + return nullptr; + + if (!m_objectsModel) { + m_objectsModel = new ObjectListModel(g_StudioApp.GetCore(), + GetDoc()->GetSceneInstance(), this); + } + + if (!m_targetObjectBrowser) + m_targetObjectBrowser = new ObjectBrowserView(this); + + m_targetObjectBrowser->setModel(m_objectsModel); + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto instanceHandle = GetBridge()->GetInstance(actionInfo.m_Owner, + actionInfo.m_TargetObject); + m_targetObjectBrowser->disconnect(); + m_targetObjectBrowser->selectAndExpand(instanceHandle, actionInfo.m_Owner); + CDialogs::showWidgetBrowser(this, m_targetObjectBrowser, point); + m_activeBrowser = m_targetObjectBrowser; + + connect(m_targetObjectBrowser, &ObjectBrowserView::selectionChanged, + this, &ActionView::OnTargetSelectionChanged); + // update also pathtype in the target object when changed from UI + connect(m_targetObjectBrowser, &ObjectBrowserView::pathTypeChanged, + this, &ActionView::OnTargetSelectionChanged); + + return m_targetObjectBrowser; +} + +void ActionView::OnTargetSelectionChanged() +{ + auto selectedItem = m_targetObjectBrowser->selectedHandle(); + setTargetObject(m_objRefHelper->GetAssetRefValue( + selectedItem, m_itemHandle, + (CRelativePathTools::EPathType)(m_targetObjectBrowser->pathType()))); + resetFiredEvent(); +} + +void ActionView::OnTriggerSelectionChanged() +{ + auto selectedItem = m_triggerObjectBrowser->selectedHandle(); + setTriggerObject(m_objRefHelper->GetAssetRefValue( + selectedItem, m_itemHandle, + (CRelativePathTools::EPathType)(m_triggerObjectBrowser->pathType()))); + resetFiredEvent(); +} + +void ActionView::showContextMenu(int x, int y) +{ + updateActionStates(); + CActionContextMenu contextMenu(QQuickWidget::actions(), this); + contextMenu.exec(mapToGlobal({x, y})); +} + +QObject *ActionView::showEventBrowser(const QPoint &point) +{ + if (!m_itemHandle.Valid()) + return nullptr; + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TriggerObject); + + if (!instanceHandle.Valid()) + return nullptr; + + if (!m_eventsModel) + m_eventsModel = new EventsModel(this); + + qt3dsdm::TEventHandleList eventList; + bridge->GetEvents(instanceHandle, eventList); + m_eventsModel->setEventList(eventList); + + if (!m_eventsBrowser) + m_eventsBrowser = new EventsBrowserView(this); + + m_eventsBrowser->setModel(m_eventsModel); + + m_eventsBrowser->disconnect(); + m_eventsBrowser->selectAndExpand(QString::fromStdWString(actionInfo.m_Event)); + CDialogs::showWidgetBrowser(this, m_eventsBrowser, point); + m_activeBrowser = m_eventsBrowser; + + connect(m_eventsBrowser, &EventsBrowserView::selectionChanged, + this, [this] { + if (m_eventsBrowser->canCommit()) + setEvent(qt3dsdm::Qt3DSDMEventHandle(m_eventsBrowser->selectedHandle())); + }); + + return m_eventsBrowser; +} + +QObject *ActionView::showHandlerBrowser(const QPoint &point) +{ + if (!m_itemHandle.Valid()) + return nullptr; + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject); + + if (!instanceHandle.Valid()) + return nullptr; + + if (!m_handlersModel) + m_handlersModel = new EventsModel(this); + + qt3dsdm::THandlerHandleList handlerList; + bridge->GetHandlers(instanceHandle, handlerList); + m_handlersModel->setHandlerList(handlerList); + + if (!m_handlerBrowser) + m_handlerBrowser = new EventsBrowserView(this); + + m_handlerBrowser->setModel(m_handlersModel); + + m_handlerBrowser->disconnect(); + m_handlerBrowser->selectAndExpand(QString::fromStdWString(actionInfo.m_Handler)); + CDialogs::showWidgetBrowser(this, m_handlerBrowser, point); + m_activeBrowser = m_handlerBrowser; + + connect(m_handlerBrowser, &EventsBrowserView::selectionChanged, + this, [this] { + if (m_handlerBrowser->canCommit()) + setHandler(qt3dsdm::Qt3DSDMHandlerHandle(m_handlerBrowser->selectedHandle())); + }); + + return m_handlerBrowser; +} + +QObject *ActionView::showEventBrowserForArgument(int handle, const QPoint &point) +{ + if (!m_itemHandle.Valid()) + return nullptr; + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject); + + if (!instanceHandle.Valid()) + return nullptr; + + if (!m_fireEventsModel) + m_fireEventsModel = new EventsModel(this); + + qt3dsdm::TEventHandleList eventList; + bridge->GetEvents(instanceHandle, eventList); + m_fireEventsModel->setEventList(eventList); + + if (!m_fireEventsBrowser) + m_fireEventsBrowser = new EventsBrowserView(this); + + m_fireEventsBrowser->setModel(m_fireEventsModel); + m_fireEventsBrowser->setHandle(handle); + + qt3dsdm::SValue oldValue; + GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, oldValue); + + QString eventName; + for (Qt3DSDMEventHandle eventHandle : eventList) { + if (oldValue == eventHandle.GetHandleValue()) { + qt3dsdm::SEventInfo eventInfo = bridge->GetEventInfo(eventHandle); + eventName = QString::fromWCharArray(eventInfo.m_FormalName.wide_str()); + if (eventName.isEmpty()) + eventName = QString::fromWCharArray(eventInfo.m_Name.wide_str()); + } + } + m_fireEventsBrowser->disconnect(); + m_fireEventsBrowser->selectAndExpand(eventName); + CDialogs::showWidgetBrowser(this, m_fireEventsBrowser, point); + m_activeBrowser = m_fireEventsBrowser; + + connect(m_fireEventsBrowser, &EventsBrowserView::selectionChanged, + this, [this, handle] { + setArgumentValue(handle, qt3dsdm::Qt3DSDMEventHandle( + m_fireEventsBrowser->selectedHandle()).GetHandleValue()); + }); + + return m_fireEventsBrowser; +} + +void ActionView::updateFiredEvent() +{ + if (!m_itemHandle.Valid()) + return; + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + if (actionInfo.m_Handler != L"Fire Event") { + m_firedEvent = tr("[Unknown event]"); + return; + } + + const auto doc = GetDoc(); + if (!doc->isValid()) + return; + + const auto bridge = GetBridge(); + const auto handlerHandle = bridge->ResolveHandler(actionInfo); + IActionCore *actionCore = doc->GetStudioSystem()->GetActionCore(); + + if (handlerHandle.Valid()) { + for (const auto &argHandle: actionInfo.m_HandlerArgs) { + const auto &argumentInfo = actionCore->GetHandlerArgumentInfo(argHandle); + DataModelDataType::Value theArgType(GetValueType(argumentInfo.m_Value)); + SValue theArgValue(argumentInfo.m_Value); + if (argumentInfo.m_ArgType == HandlerArgumentType::Event) { + theArgType = DataModelDataType::String; + auto theEventHandle = get<qt3ds::QT3DSI32>(argumentInfo.m_Value); + theArgValue = SValue(std::make_shared<CDataStr>( + bridge->GetEventInfo(theEventHandle).m_Name.wide_str())); + m_firedEvent = theArgValue.toQVariant().toString(); + Q_EMIT firedEventChanged(); + } + } + } +} + +void ActionView::updateFiredEventFromHandle(int handle) +{ + m_firedEvent = QString::fromWCharArray( + GetBridge()->GetEventInfo(handle).m_FormalName.wide_str()); + Q_EMIT firedEventChanged(); +} + +void ActionView::resetFiredEvent() +{ + m_firedEvent = tr("[Unknown Event]"); + Q_EMIT firedEventChanged(); +} + +void ActionView::OnNewPresentation() +{ + // Register callback + qt3dsdm::IStudioFullSystemSignalProvider *theSignalProvider = + g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalProvider(); + m_connections.push_back(theSignalProvider->ConnectActionCreated( + std::bind(&ActionView::OnActionAdded, this, + std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3))); + m_connections.push_back(theSignalProvider->ConnectActionDeleted( + std::bind(&ActionView::OnActionDeleted, this, + std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3))); + m_connections.push_back(theSignalProvider->ConnectTriggerObjectSet( + std::bind(&ActionView::OnActionModified, this, + std::placeholders::_1))); + m_connections.push_back(theSignalProvider->ConnectTargetObjectSet( + std::bind(&ActionView::OnActionModified, this, + std::placeholders::_1))); + m_connections.push_back(theSignalProvider->ConnectEventSet( + std::bind(&ActionView::OnActionModified, this, + std::placeholders::_1))); + m_connections.push_back(theSignalProvider->ConnectHandlerSet( + std::bind(&ActionView::OnActionModified, this, + std::placeholders::_1))); + m_connections.push_back(theSignalProvider->ConnectHandlerArgumentValueSet( + std::bind(&ActionView::OnHandlerArgumentModified, this, + std::placeholders::_1))); + m_connections.push_back(theSignalProvider->ConnectInstancePropertyValue( + std::bind(&ActionView::OnInstancePropertyValueChanged, this, + std::placeholders::_1, + std::placeholders::_2))); + m_connections.push_back(theSignalProvider->ConnectInstanceDeleted( + std::bind(&ActionView::OnInstanceDeleted, this, + std::placeholders::_1))); + CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch(); + m_connections.push_back(theDispatch->ConnectSelectionChange( + std::bind(&ActionView::OnSelectionSet, this, + std::placeholders::_1))); + + auto assetGraph = g_StudioApp.GetCore()->GetDoc()->GetAssetGraph(); + m_connections.push_back(assetGraph->ConnectChildAdded( + std::bind(&ActionView::onAssetGraphChanged, this))); + m_connections.push_back(assetGraph->ConnectChildRemoved( + std::bind(&ActionView::onAssetGraphChanged, this))); +} + +void ActionView::OnClosingPresentation() +{ + m_connections.clear(); +} + +void ActionView::OnSelectionSet(Q3DStudio::SSelectedValue inSelectable) +{ + qt3dsdm::Qt3DSDMInstanceHandle theInstance; + std::vector<qt3dsdm::Qt3DSDMInstanceHandle> instances; + + switch (inSelectable.getType()) { + case Q3DStudio::SelectedValueTypes::Instance: + theInstance = inSelectable.getData<qt3dsdm::Qt3DSDMInstanceHandle>(); + break; + case Q3DStudio::SelectedValueTypes::MultipleInstances: + instances = inSelectable.getData<std::vector<qt3dsdm::Qt3DSDMInstanceHandle>>(); + // handling only if we have one selected element. + if (instances.size() == 1) + theInstance = instances[0]; + break; + case Q3DStudio::SelectedValueTypes::Slide: { + qt3dsdm::Qt3DSDMSlideHandle theSlideHandle = + inSelectable.getData<Q3DStudio::SSlideInstanceWrapper>().m_Slide; + // Get the owning component instance + CClientDataModelBridge *theBridge = GetBridge(); + qt3dsdm::SLong4 theComponentGuid = theBridge->GetComponentGuid(theSlideHandle); + Q_ASSERT(theComponentGuid.Valid()); + theInstance = theBridge->GetInstanceByGUID(theComponentGuid); + Q_ASSERT(theInstance.Valid()); + } + break; + default: + // Clear selection on selecting other types or nothing at all + break; + }; + + setItem(theInstance); +} + +void ActionView::OnActionAdded(qt3dsdm::Qt3DSDMActionHandle inAction, + qt3dsdm::Qt3DSDMSlideHandle inSlide, + qt3dsdm::Qt3DSDMInstanceHandle inOwner) +{ + CDoc *theDoc = GetDoc(); + qt3dsdm::CStudioSystem *theStudioSystem = theDoc->GetStudioSystem(); + + qt3dsdm::Qt3DSDMSlideHandle theCurrentSlide = theDoc->GetActiveSlide(); + qt3dsdm::Qt3DSDMSlideHandle theMasterSlideOfAction = + theStudioSystem->GetSlideSystem()->GetMasterSlide(inSlide); + qt3dsdm::Qt3DSDMSlideHandle theMasterOfCurrentSlide = + theStudioSystem->GetSlideSystem()->GetMasterSlide(theCurrentSlide); + + if (!m_activeBrowser.isNull() && m_activeBrowser->isVisible()) { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } + + if (inOwner == m_itemHandle // the action is added to current viewed instance + && (theCurrentSlide == inSlide // and is added to the current viewed slide + || (theMasterSlideOfAction == inSlide + && theMasterOfCurrentSlide == theMasterSlideOfAction))) { + // or it is added to the master of the current viewed slide + m_actionsModel->addAction(inAction); + } +} + +void ActionView::OnActionDeleted(qt3dsdm::Qt3DSDMActionHandle inAction, + qt3dsdm::Qt3DSDMSlideHandle inSlide, + qt3dsdm::Qt3DSDMInstanceHandle inOwner) +{ + Q_UNUSED(inSlide); + Q_UNUSED(inOwner); + + if (!m_activeBrowser.isNull() && m_activeBrowser->isVisible()) { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } + m_actionsModel->removeAction(inAction); +} + +void ActionView::OnActionModified(qt3dsdm::Qt3DSDMActionHandle inAction) +{ + if (!m_itemHandle.Valid()) + return; + + if (GetDoc()->GetStudioSystem()->GetActionCore()->HandleValid(inAction)) { + if (!m_activeBrowser.isNull() && m_activeBrowser->isVisible()) { + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + if (!actionInfo.m_Instance.Valid()) { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } else { + // Update the selection in an active browser dialog + if (m_activeBrowser == m_triggerObjectBrowser) { + const auto instanceHandle = GetBridge()->GetInstance( + actionInfo.m_Owner, actionInfo.m_TriggerObject); + m_triggerObjectBrowser->selectAndExpand(instanceHandle, actionInfo.m_Owner); + } else if (m_activeBrowser == m_targetObjectBrowser) { + const auto instanceHandle = GetBridge()->GetInstance( + actionInfo.m_Owner, actionInfo.m_TargetObject); + m_targetObjectBrowser->selectAndExpand(instanceHandle, actionInfo.m_Owner); + } else if (m_activeBrowser == m_eventsBrowser) { + m_eventsBrowser->selectAndExpand(QString::fromStdWString(actionInfo.m_Event)); + } else if (m_activeBrowser == m_handlerBrowser) { + m_handlerBrowser->selectAndExpand( + QString::fromStdWString(actionInfo.m_Handler)); + } + } + } + m_actionsModel->updateAction(inAction); + emitActionChanged(); + } +} + +void ActionView::OnHandlerArgumentModified(qt3dsdm::Qt3DSDMHandlerArgHandle inHandlerArgument) +{ + if (!m_itemHandle.Valid()) + return; + + if (!m_fireEventsBrowser.isNull() && m_activeBrowser == m_fireEventsBrowser + && m_activeBrowser->isVisible()) { + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + + // m_fireEventsBrowser needs to be closed if another type of target handler is chosen. + // Other browsers will remain valid always as long as the action is selected. + if (actionInfo.m_Handler != L"Fire Event") { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } else { + // Update the selection in an active browser dialog + const auto bridge = GetBridge(); + const auto instanceHandle = bridge->GetInstance(actionInfo.m_Owner, + actionInfo.m_TargetObject); + qt3dsdm::TEventHandleList eventList; + bridge->GetEvents(instanceHandle, eventList); + qt3dsdm::SValue value; + GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue( + m_fireEventsBrowser->handle(), value); + QString eventName; + for (Qt3DSDMEventHandle eventHandle : eventList) { + if (value == eventHandle.GetHandleValue()) { + qt3dsdm::SEventInfo eventInfo = bridge->GetEventInfo(eventHandle); + eventName = QString::fromWCharArray(eventInfo.m_FormalName.wide_str()); + if (eventName.isEmpty()) + eventName = QString::fromWCharArray(eventInfo.m_Name.wide_str()); + } + } + m_fireEventsBrowser->selectAndExpand(eventName); + } + } + emitActionChanged(); +} + +void ActionView::OnInstancePropertyValueChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty) +{ + if (!m_itemHandle.Valid() || m_itemHandle != inInstance) + return; + + auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge(); + if (inProperty == bridge->GetNameProperty()) + Q_EMIT itemTextChanged(); + + emitActionChanged(); +} + +void ActionView::OnInstanceDeleted(qt3dsdm::Qt3DSDMInstanceHandle inInstance) +{ + // Clear the model on instance deletion + if (inInstance == m_itemHandle) { + qt3dsdm::Qt3DSDMInstanceHandle noInstance; + setItem(noInstance); + } +} + +void ActionView::copyAction() +{ + if (!m_itemHandle.Valid()) + return; + + auto theTempAPFile = + GetDoc()->GetDocumentReader().CopyAction(m_actionsModel->actionAt(m_currentActionIndex), + GetDoc()->GetActiveSlide()); + Qt3DSFile theFile(theTempAPFile); + CStudioClipboard::CopyActionToClipboard(theFile); + updateActionStates(); +} + +void ActionView::cutAction() +{ + if (!m_itemHandle.Valid()) + return; + + copyAction(); + auto action = m_actionsModel->actionAt(m_currentActionIndex); + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Cut Action"))->DeleteAction(action); + updateActionStates(); +} + +void ActionView::pasteAction() +{ + if (!m_itemHandle.Valid()) + return; + + Qt3DSFile theTempAPFile = CStudioClipboard::GetActionFromClipboard(); + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*GetDoc(), QObject::tr("Paste Action")) + ->PasteAction(theTempAPFile.GetAbsolutePath(), m_itemHandle); + updateActionStates(); +} + +void ActionView::setTriggerObject(const qt3dsdm::SObjectRefType &object) +{ + auto action = m_actionsModel->actionAt(m_currentActionIndex); + if (!action.Valid()) + return; + + if (!m_triggerObjectBrowser.isNull() && m_triggerObjectBrowser->canCommit()) { + auto core = g_StudioApp.GetCore(); + auto theBridge = GetBridge(); + + auto theCmd = new CCmdDataModelActionSetTriggerObject(GetDoc(), action, object); + const SActionInfo &theActionInfo + = GetDoc()->GetStudioSystem()->GetActionCore()->GetActionInfo(action); + + Qt3DSDMInstanceHandle theBaseInstance = theActionInfo.m_Owner; + Qt3DSDMInstanceHandle theObjectInstance = theBridge->GetInstance(theBaseInstance, object); + Qt3DSDMInstanceHandle theOldInstance = theBridge->GetInstance(theBaseInstance, + theActionInfo.m_TargetObject); + // old instance and object instance could be the same, for example if user changes the type + // from Absolute to Path. In this case we don't need to reset handler or event. + if (theOldInstance != theObjectInstance) { + theCmd->ResetEvent( + theBridge->GetDefaultEvent(theObjectInstance, theActionInfo.m_Event)); + } + + core->ExecuteCommand(theCmd); + } + emitActionChanged(); +} + +void ActionView::setTargetObject(const qt3dsdm::SObjectRefType &object) +{ + auto action = m_actionsModel->actionAt(m_currentActionIndex); + if (!action.Valid()) + return; + + if (!m_targetObjectBrowser.isNull() && m_targetObjectBrowser->canCommit()) { + auto core = g_StudioApp.GetCore(); + auto doc = GetDoc(); + auto theBridge = GetBridge(); + + auto theCmd = new CCmdDataModelActionSetTargetObject(doc, action, object); + const SActionInfo &theActionInfo = doc->GetStudioSystem()->GetActionCore()->GetActionInfo( + action); + + Qt3DSDMInstanceHandle theBaseInstance = theActionInfo.m_Owner; + Qt3DSDMInstanceHandle theObjectInstance = theBridge->GetInstance(theBaseInstance, object); + Qt3DSDMInstanceHandle theOldInstance = theBridge->GetInstance(theBaseInstance, + theActionInfo.m_TargetObject); + // old instance and object instance could be the same, for example if user changes the type + // from Absolute to Path. In this case we don't need to reset handler or event. + if (theOldInstance != theObjectInstance) { + theCmd->ResetHandler( + theBridge->GetDefaultHandler(theObjectInstance, theActionInfo.m_Handler)); + } + + core->ExecuteCommand(theCmd); + } + emitActionChanged(); +} + +void ActionView::setEvent(const Qt3DSDMEventHandle &event) +{ + if (!event.Valid()) + return; + + auto doc = GetDoc(); + const auto action = m_actionsModel->actionAt(m_currentActionIndex); + CCmd *theCmd = new CCmdDataModelActionSetEvent(doc, action, + doc->GetStudioSystem() + ->GetActionMetaData() + ->GetEventInfo(event) + ->m_Name.wide_str()); + g_StudioApp.GetCore()->ExecuteCommand(theCmd); +} + +void ActionView::setHandler(const Qt3DSDMHandlerHandle &handler) +{ + if (!handler.Valid()) + return; + + auto doc = GetDoc(); + const auto action = m_actionsModel->actionAt(m_currentActionIndex); + wstring handlerName(doc->GetStudioSystem()->GetActionMetaData()->GetHandlerInfo(handler) + ->m_Name.wide_str()); + CCmdDataModelActionSetHandler *theCmd = + new CCmdDataModelActionSetHandler(doc, action, handlerName); + theCmd->ResetHandler(handlerName); // reset the handler args + + g_StudioApp.GetCore()->ExecuteCommand(theCmd); +} + +QVariant ActionView::handlerArgumentValue(int handle) const +{ + qt3dsdm::SValue value; + GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, value); + return value.toQVariant(); +} + +void ActionView::updateHandlerArguments() +{ + m_currentPropertyValueHandle = 0; + m_currentPropertyNameHandle = 0; + m_handlerArguments.clear(); + const auto doc = GetDoc(); + if (!doc->isValid() || !m_itemHandle.Valid()) + return; + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + const auto bridge = GetBridge(); + const auto handlerHandle = bridge->ResolveHandler(actionInfo); + IActionCore *actionCore = doc->GetStudioSystem()->GetActionCore(); + + if (handlerHandle.Valid()) { + auto newMetaData = doc->GetStudioSystem()->GetActionMetaData(); + + for (const auto &argHandle: actionInfo.m_HandlerArgs) { + const auto &argumentInfo = actionCore->GetHandlerArgumentInfo(argHandle); + Option<SMetaDataHandlerArgumentInfo> argMetaData( + newMetaData->FindHandlerArgumentByName(handlerHandle, argumentInfo.m_Name)); + + HandlerArgument argument; + argument.m_handle = argHandle; + argument.m_type = argMetaData->m_ArgType; + argument.m_name = QString::fromWCharArray(argumentInfo.m_Name.wide_str()); + argument.m_value = argumentInfo.m_Value.toQVariant(); + argument.m_completeType = argMetaData->m_CompleteType; + m_handlerArguments.append(QVariant::fromValue(argument)); + } + } +} + +void ActionView::emitActionChanged() +{ + m_actionChangedCompressionTimer.start(); +} + +void ActionView::setArgumentValue(int handle, const QVariant &value) +{ + if (!m_itemHandle.Valid()) + return; + + if (handle == 0) + return; + + qt3dsdm::SValue sValue(value); + qt3dsdm::SValue oldValue; + GetDoc()->GetStudioSystem()->GetActionCore()->GetHandlerArgumentValue(handle, oldValue); + + if (!Equals(oldValue, sValue)) { + CCmd *theCmd = + new CCmdDataModelActionSetArgumentValue(GetDoc(), handle, sValue); + g_StudioApp.GetCore()->ExecuteCommand(theCmd); + } + + const auto actionInfo = m_actionsModel->actionInfoAt(m_currentActionIndex); + if (actionInfo.m_Handler == L"Fire Event") { + if (value.toInt()) + updateFiredEventFromHandle(value.toInt()); + } +} + +CDoc *ActionView::GetDoc() +{ + return g_StudioApp.GetCore()->GetDoc(); +} + +CClientDataModelBridge *ActionView::GetBridge() +{ + return GetDoc()->GetStudioSystem()->GetClientDataModelBridge(); +} + +void ActionView::initialize() +{ + CStudioPreferences::setQmlContextProperties(rootContext()); + rootContext()->setContextProperty(QStringLiteral("_parentView"), this); + rootContext()->setContextProperty(QStringLiteral("_resDir"), StudioUtils::resourceImageUrl()); + rootContext()->setContextProperty(QStringLiteral("_tabOrderHandler"), tabOrderHandler()); + rootContext()->setContextProperty(QStringLiteral("_mouseHelper"), &m_mouseHelper); + m_mouseHelper.setWidget(this); + + QString shiftKey(QStringLiteral("Shift+")); +#ifdef Q_OS_MACOS + shiftKey = "⇧"; +#endif + rootContext()->setContextProperty(QStringLiteral("_shiftKey"), shiftKey); + qmlRegisterUncreatableType<qt3dsdm::HandlerArgumentType>( + "Qt3DStudio", 1, 0, "HandlerArgumentType", + QStringLiteral("HandlerArgumentType is an enum container")); + qmlRegisterUncreatableType<qt3dsdm::DataModelDataType>( + "Qt3DStudio", 1, 0, "DataModelDataType", + QStringLiteral("DataModelDataType is an enum container")); + qmlRegisterUncreatableType<qt3dsdm::AdditionalMetaDataType>( + "Qt3DStudio", 1, 0, "AdditionalMetaDataType", + QStringLiteral("AdditionalMetaDataType is an enum container")); + qmlRegisterUncreatableType<PropertyInfo>( + "Qt3DStudio", 1, 0, "PropertyInfo", + QStringLiteral("PropertyInfo is not creatable in QML")); + qmlRegisterUncreatableType<qt3dsdm::CompleteMetaDataType>( + "Qt3DStudio", 1, 0, "CompleteMetaDataType", + QStringLiteral("CompleteMetaDataType is an enum container")); + engine()->addImportPath(StudioUtils::qmlImportPath()); + setSource(QUrl(QStringLiteral("qrc:/Palettes/Action/ActionView.qml"))); +} + +QStringList ActionView::slideNames() +{ + if (!m_itemHandle.Valid()) + return {}; + + std::list<Q3DStudio::CString> outSlideNames; + QStringList slideNames; + CClientDataModelBridge *theBridge = GetBridge(); + const auto action = m_actionsModel->actionAt(m_currentActionIndex); + + theBridge->GetSlideNamesOfAction(action, outSlideNames); + + for (auto slideName : outSlideNames) + slideNames.append(slideName.toQString()); + + return slideNames; +} + +int ActionView::slideNameToIndex(const QString &name) +{ + const auto slides = slideNames(); // KDAB_TODO cache it + return slides.indexOf(name); +} + +bool ActionView::toolTipsEnabled() +{ + return CStudioPreferences::ShouldShowTooltips(); +} + +void ActionView::updateActionStates() +{ + bool hasValidAction = (m_currentActionIndex != -1) && m_itemHandle.Valid(); + m_actionCopy->setEnabled(hasValidAction); + m_actionCut->setEnabled(hasValidAction); + m_actionDel->setEnabled(hasValidAction); + // Allow paste action even if item is not valid (list of actions is empty) + m_actionPaste->setEnabled(CStudioClipboard::CanPasteAction()); +} + +// m_propertyValueInvalid flag indicates that property value is changing and +// may not be valid if queried at the moment. It is used to prevent QML errors +// about invalid value types when changing property handlers. +void ActionView::setPropertyValueInvalid(bool invalid) +{ + if (invalid != m_propertyValueInvalid) { + m_propertyValueInvalid = invalid; + Q_EMIT propertyValueInvalidChanged(); + } +} + +// This is used to set m_propertyValueInvalid to false asynchronously +void ActionView::clearPropertyValueInvalid() +{ + setPropertyValueInvalid(false); +} + +void ActionView::onAssetGraphChanged() +{ + // Changes to asset graph invalidate the object browser model, so close it if it is open + if (!m_activeBrowser.isNull() && m_activeBrowser->isVisible() + && (m_activeBrowser == m_targetObjectBrowser + || m_activeBrowser == m_triggerObjectBrowser)) { + m_activeBrowser->close(); + m_activeBrowser.clear(); + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.h b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.h new file mode 100644 index 00000000..ab2976f3 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef ACTIONVIEW_H +#define ACTIONVIEW_H + +#include <QtQuickWidgets/qquickwidget.h> +#include <QtGui/qcolor.h> +#include <QtCore/qpointer.h> +#include <QtCore/qtimer.h> + +#include "Qt3DSCommonPrecompile.h" +#include "DispatchListeners.h" +#include "EventsBrowserView.h" +#include "EventsModel.h" +#include "ObjectBrowserView.h" +#include "ObjectListModel.h" +#include "PropertyModel.h" +#include "SelectedValueImpl.h" +#include "Qt3DSDMHandles.h" +#include "Qt3DSDMSignals.h" +#include "Qt3DSDMStudioSystem.h" +#include "Qt3DSDMMetaDataTypes.h" +#include "TabOrderHandler.h" +#include "MouseHelper.h" + +class ActionModel; +class CClientDataModelBridge; +class CCore; +class CDoc; +class IObjectReferenceHelper; + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) + +struct HandlerArgument { + Q_PROPERTY(qt3dsdm::HandlerArgumentType::Value type MEMBER m_type FINAL) + Q_PROPERTY(QString name MEMBER m_name FINAL) + Q_PROPERTY(int handle MEMBER m_handle FINAL) + Q_PROPERTY(QVariant value MEMBER m_value FINAL) + Q_PROPERTY(qt3dsdm::CompleteMetaDataType::Enum completeType MEMBER m_completeType FINAL) + + qt3dsdm::Qt3DSDMHandlerArgHandle m_handle; + qt3dsdm::HandlerArgumentType::Value m_type; + qt3dsdm::CompleteMetaDataType::Enum m_completeType; + QString m_name; + QVariant m_value; + + Q_GADGET +}; + +Q_DECLARE_METATYPE(HandlerArgument) + +class ActionView : public QQuickWidget, + public CPresentationChangeListener, + public TabNavigable +{ + Q_OBJECT + + Q_PROPERTY(QAbstractItemModel *actionsModel READ actionsModel NOTIFY itemChanged FINAL) + Q_PROPERTY(QAbstractItemModel *propertyModel READ propertyModel NOTIFY propertyModelChanged FINAL) + Q_PROPERTY(QString itemIcon READ itemIcon NOTIFY itemChanged FINAL) + Q_PROPERTY(QString itemText READ itemText NOTIFY itemTextChanged FINAL) + Q_PROPERTY(QColor itemColor READ itemColor NOTIFY itemChanged FINAL) + Q_PROPERTY(bool hasItem MEMBER m_hasItem NOTIFY hasItemChanged FINAL) + Q_PROPERTY(QString triggerObjectName READ triggerObjectName NOTIFY actionChanged FINAL) + Q_PROPERTY(QString targetObjectName READ targetObjectName NOTIFY actionChanged FINAL) + Q_PROPERTY(QString eventName READ eventName NOTIFY actionChanged FINAL) + Q_PROPERTY(QString handlerName READ handlerName NOTIFY actionChanged FINAL) + Q_PROPERTY(QVariantList handlerArguments READ handlerArguments NOTIFY actionChanged FINAL) + Q_PROPERTY(PropertyInfo property READ property NOTIFY propertyChanged FINAL) + Q_PROPERTY(QString firedEvent MEMBER m_firedEvent NOTIFY firedEventChanged FINAL) + Q_PROPERTY(bool propertyValueInvalid READ isPropertyValueInvalid NOTIFY propertyValueInvalidChanged FINAL) + +public: + ActionView(const QSize &preferredSize, QWidget *parent = nullptr); + ~ActionView() override; + + QSize sizeHint() const override; + + void setItem(const qt3dsdm::Qt3DSDMInstanceHandle &handle); + QString itemIcon() const; + QString itemText() const; + QColor itemColor() const; + QAbstractItemModel *actionsModel() const; + QAbstractItemModel *propertyModel() const; + QString targetObjectName() const; + QString triggerObjectName() const; + QString eventName() const; + QString handlerName() const; + QVariantList handlerArguments() const; + PropertyInfo property() const; + bool isPropertyValueInvalid() const; + + Q_INVOKABLE void setCurrentActionIndex(int index); + Q_INVOKABLE void setCurrentPropertyIndex(int handle, int index); + Q_INVOKABLE void addAction(); + Q_INVOKABLE void deleteAction(int index); + Q_INVOKABLE QObject *showTriggerObjectBrowser(const QPoint &point); + Q_INVOKABLE QObject *showTargetObjectBrowser(const QPoint &point); + Q_INVOKABLE void showContextMenu(int x, int y); + Q_INVOKABLE QObject *showEventBrowser(const QPoint &point); + Q_INVOKABLE QObject *showHandlerBrowser(const QPoint &point); + Q_INVOKABLE QObject *showEventBrowserForArgument(int handle, const QPoint &point); + Q_INVOKABLE void setArgumentValue(int handle, const QVariant &value); + Q_INVOKABLE QStringList slideNames(); + Q_INVOKABLE int slideNameToIndex(const QString &name); + Q_INVOKABLE bool toolTipsEnabled(); + + // CPresentationChangeListener + void OnNewPresentation() override; + void OnClosingPresentation() override; + + // ISelectionChangeListener + void OnSelectionSet(Q3DStudio::SSelectedValue inSelectable); + + // Action callback + void OnActionAdded(qt3dsdm::Qt3DSDMActionHandle inAction, qt3dsdm::Qt3DSDMSlideHandle inSlide, + qt3dsdm::Qt3DSDMInstanceHandle inOwner); + void OnActionDeleted(qt3dsdm::Qt3DSDMActionHandle inAction, qt3dsdm::Qt3DSDMSlideHandle inSlide, + qt3dsdm::Qt3DSDMInstanceHandle inOwner); + void OnActionModified(qt3dsdm::Qt3DSDMActionHandle inAction); + void OnHandlerArgumentModified(qt3dsdm::Qt3DSDMHandlerArgHandle inHandlerArgument); + void OnInstancePropertyValueChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty); + void OnInstanceDeleted(qt3dsdm::Qt3DSDMInstanceHandle inInstance); + void OnTargetSelectionChanged(); + void OnTriggerSelectionChanged(); + +protected: + void focusInEvent(QFocusEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + bool event(QEvent *event) override; + +Q_SIGNALS: + void itemChanged(); + void itemTextChanged(); + void actionChanged(); + void propertyModelChanged(); + void propertyChanged(); + void firedEventChanged(); + void hasItemChanged(); + void propertyValueInvalidChanged(); + void dialogCurrentColorChanged(const QColor &newColor); + +private Q_SLOTS: + void copyAction(); + void cutAction(); + void pasteAction(); + +private: + void setTriggerObject(const qt3dsdm::SObjectRefType &object); + void setTargetObject(const qt3dsdm::SObjectRefType &object); + void setEvent(const qt3dsdm::Qt3DSDMEventHandle &event); + void setHandler(const qt3dsdm::Qt3DSDMHandlerHandle &handler); + QVariant handlerArgumentValue(int handle) const; + void updateHandlerArguments(); + void emitActionChanged(); + void updateFiredEvent(); + void resetFiredEvent(); + void updateFiredEventFromHandle(int handle); + void updateActionStates(); + void setPropertyValueInvalid(bool invalid); + void clearPropertyValueInvalid(); + void onAssetGraphChanged(); + + static CDoc *GetDoc(); + static CClientDataModelBridge *GetBridge(); + + void initialize(); + QColor m_baseColor = QColor::fromRgb(75, 75, 75); + QColor m_selectColor = Qt::transparent; + qt3dsdm::Qt3DSDMInstanceHandle m_itemHandle; + IObjectReferenceHelper *m_objRefHelper = nullptr; + ActionModel *m_actionsModel = nullptr; + PropertyModel *m_propertyModel = nullptr; + std::vector<std::shared_ptr<qt3dsdm::ISignalConnection>> + m_connections; /// connections to the DataModel + QPointer<ObjectListModel> m_objectsModel; + QPointer<ObjectBrowserView> m_triggerObjectBrowser; + QPointer<ObjectBrowserView> m_targetObjectBrowser; + QPointer<EventsModel> m_eventsModel; + QPointer<EventsModel> m_handlersModel; + QPointer<EventsModel> m_fireEventsModel; + QPointer<EventsBrowserView> m_eventsBrowser; + QPointer<EventsBrowserView> m_handlerBrowser; + QPointer<EventsBrowserView> m_fireEventsBrowser; + int m_currentActionIndex = -1; + int m_currentPropertyIndex = -1; + qt3dsdm::Qt3DSDMHandlerArgHandle m_currentPropertyNameHandle; + qt3dsdm::Qt3DSDMHandlerArgHandle m_currentPropertyValueHandle; + QVariantList m_handlerArguments; + QTimer m_actionChangedCompressionTimer; + QString m_firedEvent; + MouseHelper m_mouseHelper; + QSize m_preferredSize; + bool m_hasItem = false; + QAction *m_actionDel; + QAction *m_actionCopy; + QAction *m_actionCut; + QAction *m_actionPaste; + bool m_propertyValueInvalid = true; + QColor m_currentColor; + QPointer<QWidget> m_activeBrowser = nullptr; +}; + +#endif // ACTIONVIEW_H diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.qml b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.qml new file mode 100644 index 00000000..a5b905b3 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/ActionView.qml @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Qt3DStudio 1.0 +import "../controls" + +Rectangle { + id: root + + color: _backgroundColor + + Item { + id: focusEater + objectName: "focusEater" + // Used to eat keyboard focus when user clicks outside any property control + } + + Flickable { + id: actionFlickable + ScrollBar.vertical: ScrollBar { + id: scrollBar + visible: size < 1.0 + } + + MouseArea { + anchors.fill: parent + z: -10 + onPressed: { + mouse.accepted = false + focusEater.forceActiveFocus(); + } + } + + anchors.fill: parent + contentHeight: contentColumn.height + + property bool scrollToBottom: false + + onContentHeightChanged: { + if (scrollToBottom) { + scrollToBottom = false; + if (contentHeight > height) + contentY = contentHeight - height; + } + } + + Column { + id: contentColumn + width: parent.width + spacing: 4 + + RowLayout { + height: _controlBaseHeight + 8 + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 4 + anchors.rightMargin: 12 + + Image { + id: headerImage + source: _parentView.itemIcon !== "" ? _resDir + _parentView.itemIcon : "" + } + + StyledLabel { + Layout.fillWidth: true + text: _parentView.itemText + color: _parentView.itemColor + } + + StyledToolButton { + enabled: actionsList.currentIndex !== -1 + enabledImage: "Action-Trash-Normal.png" + disabledImage: "Action-Trash-Disabled.png" + toolTipText: qsTr("Delete (Del)") + + onClicked: _parentView.deleteAction(actionsList.currentIndex) + } + + StyledToolButton { + enabledImage: "add.png" + disabledImage: "add-disabled.png" + toolTipText: qsTr("New Action (") + _shiftKey + "A)" + enabled: _parentView.hasItem + + onClicked: _parentView.addAction() + } + } + ListView { + id: actionsList + width: parent.width + height: count == 0 ? _controlBaseHeight : count * _controlBaseHeight + clip: true + + Connections { + target: _parentView + // Clear the action selection on item selection change + onItemChanged: actionsList.currentIndex = -1 + } + + MouseArea { + anchors.fill: parent + enabled: parent.count == 0 + + acceptedButtons: Qt.RightButton + + onClicked: { + if (mouse.button == Qt.RightButton) { + var updateMousePosition = mapToItem(actionsList, mouse.x, mouse.y) + _parentView.showContextMenu( + updateMousePosition.x, updateMousePosition.y); + } + } + } + boundsBehavior: Flickable.StopAtBounds + model: _parentView.actionsModel + + delegate: Rectangle { + id: delegateItem + objectName: "actionListDelegate" + + width: actionsList.width + height: _controlBaseHeight + color: model.index === actionsList.currentIndex ? _selectionColor + : "transparent" + + Row { + x: 10 + y: 5 + height: parent.height + width: parent.width - x + spacing: 4 + + Image { + id: visibilityIcon + + source: model.visible ? _resDir + "Toggle-HideShow.png" + : _resDir + "Toggle-HideShow-disabled.png" + + MouseArea { + anchors.fill: parent + onClicked: model.visible = !model.visible + } + } + + StyledLabel { + text: model.description + } + } + + MouseArea { + anchors.fill: parent + + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onPressed: { + actionsList.forceActiveFocus(); + } + + onClicked: { + actionFlickable.scrollToBottom = false; + actionsList.currentIndex = model.index; + _parentView.setCurrentActionIndex(model.index); + if (mouse.button == Qt.LeftButton && mouse.x < visibilityIcon.width + 10) + model.visible = !model.visible; + + if (mouse.button == Qt.RightButton) { + var updateMousePosition = mapToItem(actionsList, mouse.x, mouse.y) + _parentView.showContextMenu(updateMousePosition.x, updateMousePosition.y); + } + } + onDoubleClicked: { + actionFlickable.scrollToBottom = false; + if (mouse.button == Qt.LeftButton && mouse.x > visibilityIcon.width + 10) { + // Scroll down to bottom to show properties on double click + if (actionFlickable.contentHeight > actionFlickable.height) { + actionFlickable.contentY = (actionFlickable.contentHeight + - actionFlickable.height) + } + // Since loading new property fields takes a moment, we want + // to keep the view scrolled to bottom + // when the content height changes the next time. + actionFlickable.scrollToBottom = true; + } + } + } + } + + onCountChanged: { + if (currentIndex >= count) + currentIndex = count - 1; + } + + onCurrentIndexChanged: _parentView.setCurrentActionIndex(currentIndex); + } + + StyledMenuSeparator { + leftPadding: 12 + rightPadding: 12 + } + + Column { + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + visible: actionsList.currentIndex !== -1 + spacing: 4 + + RowLayout { + x: 12 + StyledLabel { + text: qsTr("Trigger Object") + } + BrowserCombo { + value: _parentView.triggerObjectName + onShowBrowser: activeBrowser = _parentView.showTriggerObjectBrowser( + mapToGlobal(width, 0)); + } + } + + RowLayout { + x: 12 + StyledLabel { + text: qsTr("Event") + } + BrowserCombo { + value: _parentView.eventName + onShowBrowser: activeBrowser = _parentView.showEventBrowser( + mapToGlobal(width, 0)) + } + } + } + + StyledMenuSeparator { + visible: actionsList.currentIndex !== -1 + leftPadding: 12 + rightPadding: 12 + } + + Column { + visible: actionsList.currentIndex !== -1 + width: parent.width + height: childrenRect.height + spacing: 4 + + RowLayout { + x: 12 + StyledLabel { + text: qsTr("Target Object") + } + + BrowserCombo { + value: _parentView.targetObjectName + onShowBrowser: activeBrowser = _parentView.showTargetObjectBrowser( + mapToGlobal(width, 0)) + } + } + + RowLayout { + x: 12 + StyledLabel { + text: qsTr("Handler") + } + + BrowserCombo { + value: _parentView.handlerName + onShowBrowser: activeBrowser = _parentView.showHandlerBrowser( + mapToGlobal(width, 0)) + } + } + + Component { + id: genericHandlerComponent + + HandlerGenericText { + label: parent && parent.argument.name ? parent.argument.name : "" + value: parent && parent.argument.value ? parent.argument.value : "" + + onEditingFinished: { + if (parent) + _parentView.setArgumentValue(parent.argument.handle, value) + } + } + } + + Component { + id: floatHandlerComponent + + HandlerGenericText { + label: parent && parent.argument.name ? parent.argument.name : "" + value: parent && parent.argument.value ? parent.argument.value : 0.0 + validator: DoubleValidator { + decimals: 3 + notation: DoubleValidator.StandardNotation + } + + onEditingFinished: { + if (parent) + _parentView.setArgumentValue(parent.argument.handle, value) + } + } + } + + Component { + id: signalHandlerComponent + + HandlerGenericText { + label: parent && parent.argument.name ? parent.argument.name : "" + value: parent && parent.argument.value ? parent.argument.value : "" + + onEditingFinished: { + if (parent) + _parentView.setArgumentValue(parent.argument.handle, value); + } + } + } + + Component { + id: eventHandlerComponent + + HandlerFireEvent { + label: parent && parent.argument.name ? parent.argument.name : "" + value: _parentView.firedEvent === "" ? qsTr("[Unknown Event]") + : _parentView.firedEvent + + onShowBrowser: { + if (parent && parent.argument.handle) { + activeBrowser = _parentView.showEventBrowserForArgument( + parent.argument.handle, mapToGlobal(width, 0)) + } + } + } + } + + Component { + id: slideHandlerComponent + + HandlerGoToSlide { + slideModel: _parentView.slideNames() + defaultSlideIndex: parent && parent.argument.value ? _parentView.slideNameToIndex(parent.argument.value) + : 0 + + onActivated: { + if (parent && parent.argument.handle && currentSlide) + _parentView.setArgumentValue(parent.argument.handle, currentSlide) + } + } + } + + Component { + id: checkboxHandlerComponent + + HandlerGenericCheckbox { + label: parent && parent.argument.name ? parent.argument.name : "" + checked: parent && parent.argument.value ? parent.argument.value : false + + onClicked: { + if (parent && parent.argument.handle) + _parentView.setArgumentValue(parent.argument.handle, !checked) + } + } + } + + Component { + id: propertyHandlerComponent + + HandlerProperty { + propertyModel: _parentView.propertyModel + defaultPropertyIndex: propertyModel ? propertyModel.defaultPropertyIndex : 0 + + onPropertySelected: { + if (parent && parent.argument.handle) + _parentView.setCurrentPropertyIndex(parent.argument.handle, index); + } + } + } + + Repeater { + model: _parentView.handlerArguments.length + + Loader { + x: 12 + + readonly property var argument:_parentView.handlerArguments[model.index] + + onLoaded: { + // HandlerProperty does its own tab order handling + if (argument.type !== HandlerArgumentType.Property) { + // Dynamic actions use group 0. + // We assume there is always just one tabbable argument per action, + // and the rest are dependent types. + _tabOrderHandler.clear(); + if (item.tabItem1 !== undefined) { + _tabOrderHandler.addItem(0, item.tabItem1) + if (item.tabItem2 !== undefined) { + _tabOrderHandler.addItem(0, item.tabItem2) + if (item.tabItem3 !== undefined) + _tabOrderHandler.addItem(0, item.tabItem3) + } + } + } + } + + sourceComponent: { + const handlerType = argument.type; + switch (handlerType) { + case HandlerArgumentType.None: + switch (argument.completeType) { + case CompleteMetaDataType.Boolean: + return checkboxHandlerComponent; + case CompleteMetaDataType.Float: + return floatHandlerComponent; + default: + return genericHandlerComponent; + } + case HandlerArgumentType.Event: + return eventHandlerComponent; + case HandlerArgumentType.Property: + return propertyHandlerComponent; + case HandlerArgumentType.Dependent: + return null; // no UI for Dependent type, they are the value for a property + case HandlerArgumentType.Signal: + return signalHandlerComponent; + case HandlerArgumentType.Slide: + return slideHandlerComponent + default: console.warn("KDAB_TODO implement handler for type: ", handlerType) + } + return null; + } + } + } + } + + StyledMenuSeparator { + visible: actionsList.count > 0 && actionsList.currentIndex !== -1 + leftPadding: 12 + rightPadding: 12 + } + } + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowser.qml b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowser.qml new file mode 100644 index 00000000..e5cb3998 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowser.qml @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +Rectangle { + id: root + + color: _backgroundColor + border.color: _studioColor3 + + ColumnLayout { + anchors.fill: parent + + ListView { + id: eventsList + + Layout.margins: 5 + Layout.fillWidth: true + Layout.fillHeight: true + + ScrollBar.vertical: ScrollBar {} + + boundsBehavior: Flickable.StopAtBounds + clip: true + currentIndex: _eventsBrowserView.selection + + model: _eventsBrowserView.model + + delegate: Item { + id: delegateItem + + readonly property bool isCategory: model.isCategory + + width: parent.width + height: model.parentExpanded ? _controlBaseHeight : 0 + visible: height > 0 + + Behavior on height { + NumberAnimation { + duration: 100 + easing.type: Easing.OutQuad + } + } + + Rectangle { + width: parent.width + height: parent.height + color: model.index === eventsList.currentIndex ? _selectionColor + : "transparent" + Row { + id: row + width: parent.width + height: parent.height + spacing: 5 + + Image { + id: arrow + anchors.verticalCenter: parent.verticalCenter + source: { + if (!delegateItem.isCategory) + return ""; + model.expanded ? _resDir + "arrow_down.png" + : _resDir + "arrow.png"; + } + + MouseArea { + anchors.fill: parent + onClicked: model.expanded = !model.expanded + } + } + + Image { // group icon + anchors.verticalCenter: parent.verticalCenter + source: model.icon + } + + StyledLabel { + id: name + leftPadding: isCategory ? 0 : 45 + anchors.verticalCenter: parent.verticalCenter + text: model.name + } + } + + MouseArea { + id: delegateArea + anchors.fill: parent + anchors.leftMargin: arrow.width + hoverEnabled: true + onClicked: { + if (!delegateItem.isCategory) + eventsList.currentIndex = model.index; + } + onEntered: itemDescription.text = model.description + onExited: itemDescription.text = "" + onDoubleClicked: { + if (!delegateItem.isCategory) { + eventsList.currentIndex = model.index; + _eventsBrowserView.close(); + } else { + model.expanded = !model.expanded + } + } + } + } + + } + onCurrentIndexChanged: _eventsBrowserView.selection = currentIndex + + Connections { + target: _eventsBrowserView + onSelectionChanged: { + if (eventsList.currentIndex !== _eventsBrowserView.selection) + eventsList.currentIndex = _eventsBrowserView.selection; + } + } + } + + StyledMenuSeparator { + bottomPadding: 0 + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: _controlBaseHeight + 4 + Rectangle { + anchors.fill: parent + anchors.margins: 2 + + color: _backgroundColor + + StyledLabel { + id: itemDescription + leftPadding: 6 + anchors.fill: parent + } + } + } + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.cpp new file mode 100644 index 00000000..b7151af2 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "EventsBrowserView.h" + +#include "EventsModel.h" +#include "StudioUtils.h" +#include "StudioPreferences.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qtimer.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlengine.h> + +EventsBrowserView::EventsBrowserView(QWidget *parent) : QQuickWidget(parent) +{ + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + setResizeMode(QQuickWidget::SizeRootObjectToView); + QTimer::singleShot(0, this, &EventsBrowserView::initialize); +} + +QAbstractItemModel *EventsBrowserView::model() const +{ + return m_model; +} + +void EventsBrowserView::setModel(EventsModel *model) +{ + if (m_model != model) { + m_model = model; + Q_EMIT modelChanged(); + } +} + +qt3dsdm::CDataModelHandle EventsBrowserView::selectedHandle() const +{ + const auto handleId = m_model->handleForRow(m_selection); + return handleId; +} + +void EventsBrowserView::selectAndExpand(const QString &event) +{ + // All categories are expanded by default, so let's just select + m_blockCommit = true; + setSelection(m_model->rowForEventName(event)); + m_blockCommit = false; + +} + +void EventsBrowserView::setSelection(int index) +{ + auto handleId = m_model->handleForRow(index); + if (!handleId.Valid()) { + m_selection = -1; + Q_EMIT selectionChanged(); + } else if (m_selection != index) { + m_selection = index; + Q_EMIT selectionChanged(); + } +} + +void EventsBrowserView::focusOutEvent(QFocusEvent *event) +{ + QQuickWidget::focusOutEvent(event); + QTimer::singleShot(0, this, &EventsBrowserView::close); +} + +void EventsBrowserView::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) + QTimer::singleShot(0, this, &EventsBrowserView::close); + + QQuickWidget::keyPressEvent(event); +} + +void EventsBrowserView::initialize() +{ + CStudioPreferences::setQmlContextProperties(rootContext()); + rootContext()->setContextProperty(QStringLiteral("_eventsBrowserView"), this); + rootContext()->setContextProperty(QStringLiteral("_resDir"), + StudioUtils::resourceImageUrl()); + engine()->addImportPath(StudioUtils::qmlImportPath()); + setSource(QUrl(QStringLiteral("qrc:/Palettes/Action/EventsBrowser.qml"))); +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.h b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.h new file mode 100644 index 00000000..f8a2b7fa --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/EventsBrowserView.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ +#ifndef EVENTSBROWSERVIEW_H +#define EVENTSBROWSERVIEW_H + +#include <QQuickWidget> + +#include "Qt3DSDMHandles.h" + +class EventsModel; + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) + +class EventsBrowserView : public QQuickWidget +{ + Q_OBJECT + Q_PROPERTY(QAbstractItemModel *model READ model NOTIFY modelChanged FINAL) + Q_PROPERTY(int selection READ selection WRITE setSelection NOTIFY selectionChanged FINAL) +public: + explicit EventsBrowserView(QWidget *parent = nullptr); + + QAbstractItemModel *model() const; + void setModel(EventsModel *model); + qt3dsdm::CDataModelHandle selectedHandle() const; + + void selectAndExpand(const QString &event); + + int selection() const { return m_selection; } + void setSelection(int index); + + void setHandle(int handle) { m_handle = handle; } + int handle() const { return m_handle; } + + bool canCommit() const { return !m_blockCommit; } + +Q_SIGNALS: + void modelChanged(); + void selectionChanged(); + +protected: + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + +private: + void initialize(); + EventsModel *m_model = nullptr; + QColor m_baseColor = QColor::fromRgb(75, 75, 75); + QColor m_selectColor; + int m_selection = -1; + int m_handle = -1; + bool m_blockCommit = false; +}; + +#endif // EVENTSBROWSERVIEW_H diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.cpp new file mode 100644 index 00000000..981ab95a --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.cpp @@ -0,0 +1,276 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "EventsModel.h" + +#include "ClientDataModelBridge.h" +#include "Core.h" +#include "Doc.h" +#include "StudioUtils.h" +#include "StudioApp.h" +#include "Qt3DSDMStudioSystem.h" + +EventsModel::EventsModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +void EventsModel::setEventList(const qt3dsdm::TEventHandleList &eventList) +{ + beginResetModel(); + + m_rowCount = 0; + m_events.clear(); + m_categories.clear(); + + auto studioSystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem(); + auto theBridge = studioSystem->GetClientDataModelBridge(); + auto thePos = eventList.begin(); + for (; thePos != eventList.end(); ++thePos) { + qt3dsdm::SEventInfo theEvent = theBridge->GetEventInfo(*thePos); + + CategoryInfo category; + category.name = QString::fromWCharArray(theEvent.m_Category.wide_str()); + if (!m_events.contains(category.name)) { + qt3dsdm::SCategoryInfo theCategoryMetaData = studioSystem->GetActionMetaData() + ->GetEventCategory(theEvent.m_Category); + category.icon = QString::fromWCharArray(theCategoryMetaData.m_Icon.wide_str()); + category.highlightIcon = QString::fromWCharArray(theCategoryMetaData.m_HighlightIcon.wide_str()); + category.description = QString::fromWCharArray(theCategoryMetaData.m_Description.wide_str()); + m_categories.append(category); + m_rowCount++; + } + + EventInfo eventInfo; + // Use the formal name to display, but if the formal name is not set, use the name instead + eventInfo.name = QString::fromWCharArray(theEvent.m_FormalName.wide_str()); + if (eventInfo.name.isEmpty()) + eventInfo.name = QString::fromWCharArray(theEvent.m_Name.wide_str()); + eventInfo.handle = *thePos; + + eventInfo.description = QString::fromWCharArray(theEvent.m_Description.wide_str()); + m_events[category.name].append(eventInfo); + m_rowCount++; + + //KDAB_TODO set the selection to the current event + } + + endResetModel(); +} + +void EventsModel::setHandlerList(const qt3dsdm::THandlerHandleList &handlerList) +{ + beginResetModel(); + m_rowCount = 0; + m_events.clear(); + m_categories.clear(); + + auto studioSystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem(); + auto theBridge = studioSystem->GetClientDataModelBridge(); + auto thePos = handlerList.begin(); + for (; thePos != handlerList.end(); ++thePos) { + qt3dsdm::SHandlerInfo handlerInfo = theBridge->GetHandlerInfo(*thePos); + + CategoryInfo category; + category.name = QString::fromWCharArray(handlerInfo.m_Category.wide_str()); + if (!m_events.contains(category.name)) { + qt3dsdm::SCategoryInfo theCategoryMetaData = studioSystem->GetActionMetaData() + ->GetHandlerCategory(handlerInfo.m_Category); + category.icon = QString::fromWCharArray(theCategoryMetaData.m_Icon.wide_str()); + category.highlightIcon = QString::fromWCharArray(theCategoryMetaData.m_HighlightIcon.wide_str()); + category.description = QString::fromWCharArray(theCategoryMetaData.m_Description.wide_str()); + m_categories.append(category); + m_rowCount++; + } + + EventInfo eventInfo; + // Use the formal name to display, but if the formal name is not set, use the name instead + eventInfo.name = QString::fromWCharArray(handlerInfo.m_FormalName.wide_str()); + if (eventInfo.name.isEmpty()) + eventInfo.name = QString::fromWCharArray(handlerInfo.m_Name.wide_str()); + eventInfo.handle = *thePos; + + eventInfo.description = QString::fromWCharArray(handlerInfo.m_Description.wide_str()); + m_events[category.name].append(eventInfo); + m_rowCount++; + + //KDAB_TODO set the selection to the current event + } + + endResetModel(); +} + +int EventsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_rowCount; +} + +QVariant EventsModel::data(const QModelIndex &index, int role) const +{ + if (!hasIndex(index.row(), index.column(), index.parent())) + return {}; + + const auto row = index.row(); + auto category = categoryForRow(row); + + bool isCategory = category.isValid(); + EventInfo event; + if (!isCategory) + event = eventForRow(row); + + switch (role) { + case NameRole: + return isCategory ? category.name : event.name; + case DescriptionRole: + return isCategory ? category.description: event.description; + case IconRole: + return isCategory ? StudioUtils::resourceImageUrl() + category.icon : QString(); + case HighlightedIconRole: + return isCategory ? StudioUtils::resourceImageUrl() + category.highlightIcon : QString(); + case ExpandedRole: + return isCategory ? category.expanded : false; + case ParentExpandedRole: { + if (isCategory) + return true; + for (int i = row - 1; i >= 0; i--) { + auto parentCategory = categoryForRow(i); + if (parentCategory.isValid()) + return parentCategory.expanded; + } + return false; + } + case IsCategoryRole: + return isCategory; + } + + return QVariant(); +} + +bool EventsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == ExpandedRole) { + int catRow = categoryRowForRow(index.row()); + if (catRow != -1) { + auto category = &m_categories[catRow]; + category->expanded = value.toBool(); + Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {}); + return true; + } + } + return false; +} + +QHash<int, QByteArray> EventsModel::roleNames() const +{ + auto names = QAbstractItemModel::roleNames(); + names.insert(NameRole, "name"); + names.insert(DescriptionRole, "description"); + names.insert(IconRole, "icon"); + names.insert(HighlightedIconRole, "highlightedIcon"); + names.insert(IsCategoryRole, "isCategory"); + names.insert(ExpandedRole, "expanded"); + names.insert(ParentExpandedRole, "parentExpanded"); + + return names; +} + +qt3dsdm::CDataModelHandle EventsModel::handleForRow(int row) const +{ + if (row < 0 || row >= m_rowCount) + return {}; + + auto event = eventForRow(row); + if (event.isValid()) + return event.handle; + + return {}; +} + +int EventsModel::rowForEventName(const QString &event) const +{ + int i = 0; + for (const auto &category: m_categories) { + i++; + const auto events = m_events[category.name]; + for (int j = 0; j < events.size(); j++, i++) { + if (events[j].name == event) + return i; + } + } + return i; +} + +EventsModel::CategoryInfo EventsModel::categoryForRow(int row) const +{ + int i = 0; + for (const auto &category: m_categories) { + if (i == row) + return category; + i += m_events[category.name].size(); + i++; + } + + return {}; +} + +int EventsModel::categoryRowForRow(int row) const +{ + int i = 0; + int catRow = 0; + for (const auto &category: m_categories) { + if (i == row) + return catRow; + i += m_events[category.name].size(); + i++; + catRow++; + } + + return -1; +} + +EventsModel::EventInfo EventsModel::eventForRow(int row) const +{ + if (row == 0) // first line is not an event, but a category + return {}; + + int i = 0; + for (const auto &category: m_categories) { + i++; + const auto events = m_events[category.name]; + const int index = (row - i); + if (row < i + events.size() && (index >= 0) ) { + return events[index]; + } + i += events.size(); + } + + return {}; +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.h b/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.h new file mode 100644 index 00000000..b0f2f4fb --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/EventsModel.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef EVENTSMODEL_H +#define EVENTSMODEL_H + +#include <QAbstractListModel> + +#include "Qt3DSDMHandles.h" + +/** Model for both action events and action handlers */ +class EventsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit EventsModel(QObject *parent = nullptr); + + void setEventList(const qt3dsdm::TEventHandleList &eventList); + void setHandlerList(const qt3dsdm::THandlerHandleList &handlerList); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + + enum Roles { + NameRole = Qt::DisplayRole, + DescriptionRole = Qt::UserRole + 1, + IconRole, + HighlightedIconRole, + ExpandedRole, + ParentExpandedRole, + IsCategoryRole + }; + + QHash<int, QByteArray> roleNames() const override; + + qt3dsdm::CDataModelHandle handleForRow(int row) const; + int rowForEventName(const QString &event) const; + +private: + struct EventInfo { + qt3dsdm::CDataModelHandle handle; + QString name; + QString description; + + bool isValid() const { return handle.Valid(); } + }; + + struct CategoryInfo { + QString name; + QString icon; + QString description; + QString highlightIcon; + bool expanded = true; + + bool isValid() const { return !name.isEmpty(); } + }; + + CategoryInfo categoryForRow(int row) const; + int categoryRowForRow(int row) const; + EventInfo eventForRow(int row) const; + + QHash<QString, QVector<EventInfo> > m_events; + QVector<CategoryInfo> m_categories; + int m_rowCount = 0; +}; + +#endif // EVENTSMODEL_H diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerBaseMultilineText.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerBaseMultilineText.qml new file mode 100644 index 00000000..fbab75cb --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerBaseMultilineText.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +ScrollView { + id: root + signal editingFinished + signal textChanged + property alias value: textArea.text + property Item tabItem1: textArea + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + TextArea { + id: textArea + property bool ignoreHotkeys: true + + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignTop + font.pixelSize: _fontSize + color: _textColor + selectionColor: _selectionColor + selectedTextColor: _textColor + + topPadding: 6 + bottomPadding: 6 + rightPadding: 6 + + wrapMode: TextEdit.WrapAnywhere + background: Rectangle { + anchors.fill: parent + color: textArea.enabled ? _studioColor2 : "transparent" + border.width: textArea.activeFocus ? 1 : 0 + border.color: textArea.activeFocus ? _selectionColor : _disabledColor + } + + MouseArea { + id: mouseArea + property int clickedPos + + anchors.fill: parent + preventStealing: true + onPressed: { + textArea.forceActiveFocus() + clickedPos = textArea.positionAt(mouse.x, mouse.y) + textArea.cursorPosition = clickedPos + } + onDoubleClicked: textArea.selectAll() + onPositionChanged: { + textArea.cursorPosition = textArea.positionAt(mouse.x, mouse.y) + textArea.select(clickedPos, textArea.cursorPosition) + } + } + onTextChanged: root.textChanged() + onEditingFinished: root.editingFinished() + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerEmitSignal.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerEmitSignal.qml new file mode 100644 index 00000000..16eac96e --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerEmitSignal.qml @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias value: textField.text + property Item tabItem1: textfield + + StyledLabel { + id: labelField + text: qsTr("Signal Name") + } + + StyledTextField { + id: textField + Layout.preferredWidth: _valueWidth + } +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerFireEvent.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerFireEvent.qml new file mode 100644 index 00000000..5b2ca0f6 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerFireEvent.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias value: comboField.value + property alias activeBrowser: comboField.activeBrowser + + signal showBrowser + + StyledLabel { + id: labelField + text: qsTr("Event") + } + + BrowserCombo { + id: comboField + Layout.preferredWidth: _valueWidth + value: qsTr("[Unknown Event]") + onShowBrowser: root.showBrowser() + } +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericBaseColor.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericBaseColor.qml new file mode 100644 index 00000000..8c184409 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericBaseColor.qml @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.3 + +RowLayout { + id: root + + property alias color: rect.color + property color selectedColor: "black" + property bool listenToColorChanges: false + + signal colorSelected() + signal previewColorSelected() + + Connections { + target: _parentView + onDialogCurrentColorChanged: { + if (root.listenToColorChanges) { + root.selectedColor = newColor; + root.previewColorSelected(); + } + } + } + + Rectangle { + id: rect + + width: _valueWidth / 4 + height: _controlBaseHeight + + border { + width: 1 + color: _studioColor2 + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + onClicked: { + root.listenToColorChanges = true; + _inspectorModel.suspendMaterialRename(true); + root.selectedColor = _parentView.showColorDialog(rect.color, instance, handle); + root.listenToColorChanges = false; + _inspectorModel.suspendMaterialRename(false); + root.colorSelected(); + } + } + + Image { + id: img + // Source image size is 16x16 pixels + x: parent.width - 18 + y: parent.height / 2 - 8 + source: _resDir + "arrow_down.png" + } + } + + Item { + Layout.fillWidth: true + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericCheckbox.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericCheckbox.qml new file mode 100644 index 00000000..8446f761 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericCheckbox.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property bool checked: false + property alias label: labelField.text + + signal clicked() + + StyledLabel { + id: labelField + text: qsTr("Pause") + } + + Image { + source: _resDir + (checked ? "checkbox-checked.png" : "checkbox-unchecked.png") + + MouseArea { + anchors.fill: parent + onClicked: root.clicked() + } + } + + Item { + Layout.fillWidth: true + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericColor.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericColor.qml new file mode 100644 index 00000000..cad079ee --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericColor.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias color: handlerGenericColor.color + property alias selectedColor: handlerGenericColor.selectedColor + + signal colorSelected() + signal previewColorSelected() + + StyledLabel { + id: labelField + text: qsTr("New Value") + } + + HandlerGenericBaseColor { + id: handlerGenericColor + + onColorSelected: root.colorSelected(); + onPreviewColorSelected: root.previewColorSelected(); + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericFloat.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericFloat.qml new file mode 100644 index 00000000..11ac38a5 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericFloat.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property real desiredValue: Number(floatField.text) + property real value: 0 + property int numberOfDecimal: 3 + property Item tabItem1: floatField + + signal editingFinished + signal previewValueChanged + + onValueChanged: { + // FloatTextField can set its text internally, thus breaking the binding, so + // let's set the text value explicitly each time value changes + floatField.text = Number(value).toFixed(numberOfDecimal); + } + + StyledLabel { + id: labelField + } + + FloatTextField { + id: floatField + Layout.preferredWidth: _valueWidth + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericText.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericText.qml new file mode 100644 index 00000000..738bf6a3 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGenericText.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias value: textField.text + property alias validator: textField.validator + property Item tabItem1: textField + + signal editingFinished + + onValueChanged: { + textField.text = value; + } + + StyledLabel { + id: labelField + } + + StyledTextField { + id: textField + onEditingFinished: root.editingFinished(); + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGoToSlide.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGoToSlide.qml new file mode 100644 index 00000000..6ad1564b --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerGoToSlide.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias slideModel: comboSlide.model + property string currentSlide + property int defaultSlideIndex: 0 + + signal activated() + + onDefaultSlideIndexChanged: comboSlide.currentIndex = defaultSlideIndex + + StyledLabel { + id: labelField + text: qsTr("Slide") + } + + StyledComboBox { + id: comboSlide + Layout.preferredWidth: _valueWidth + model: slideModel + + onActivated: { + currentSlide = comboSlide.textAt(currentIndex); + root.activated(); + } + } +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerMultilineText.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerMultilineText.qml new file mode 100644 index 00000000..834b1083 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerMultilineText.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +RowLayout { + id: root + + property alias label: labelField.text + property alias value: multiLine.value + property alias tabItem1: multiLine.tabItem1 + + signal editingFinished + signal textChanged + + onValueChanged: { + multiLine.value = value; + } + + StyledLabel { + id: labelField + } + + HandlerBaseMultilineText { + id: multiLine + + Layout.preferredWidth: _valueWidth + Layout.preferredHeight: _controlBaseHeight * 3 + + onTextChanged: root.textChanged(); + onEditingFinished: root.editingFinished(); + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerProperty.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerProperty.qml new file mode 100644 index 00000000..a101488e --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerProperty.qml @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import Qt3DStudio 1.0 +import "../controls" + +ColumnLayout { + id: root + + property alias propertyModel: propertyCombo.model + property int defaultPropertyIndex: 0 + + signal propertySelected(int index) + + onDefaultPropertyIndexChanged: propertyCombo.currentIndex = defaultPropertyIndex + + RowLayout { + + Layout.fillWidth: true + + StyledLabel { + text: qsTr("Property") + } + + StyledComboBox { + id: propertyCombo + textRole: "name" + onActivated: root.propertySelected(currentIndex) + onModelChanged: currentIndex = root.defaultPropertyIndex + } + } + + Component { + id: multiLineComponent + + HandlerMultilineText { + readonly property var actionProperty: parent ? _parentView.property : null + + label: parent ? parent.label : "" + value: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined ? propertyModel.value : "" + onEditingFinished: _parentView.setArgumentValue(propertyModel.valueHandle, value) + } + } + + Component { + id: fontSizeComponent + + HandlerPropertyCombo { + readonly property var actionProperty: parent ? _parentView.property : null + property var propertyValue: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined + ? propertyModel.value : "" + + label: parent ? parent.label : "" + comboModel: ["8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", + "28", "36", "48", "72", "96", "120"]; + + onValueChanged: _parentView.setArgumentValue(propertyModel.valueHandle, value) + onPropertyValueChanged: currentIndex = find(propertyValue) + } + } + + Component { + id: xyzPropertyComponent + + HandlerPropertyXYZ { + readonly property var propValue: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined + ? propertyModel.value : undefined + label: parent ? parent.label : "" + valueX: propValue !== undefined ? Number(propValue.x).toFixed(numberOfDecimal) : "0.000" + valueY: propValue !== undefined ? Number(propValue.y).toFixed(numberOfDecimal) : "0.000" + valueZ: propValue !== undefined ? Number(propValue.z).toFixed(numberOfDecimal) : "0.000" + + onPropValueChanged: { + // FloatTextField can set its text internally, thus breaking the binding, so + // let's set the text value explicitly each time value changes + if (propValue !== undefined) { + valueX = Number(propValue.x).toFixed(numberOfDecimal); + valueY = Number(propValue.y).toFixed(numberOfDecimal); + valueZ = Number(propValue.z).toFixed(numberOfDecimal); + } + } + + onEditingFinished: { + _parentView.setArgumentValue(propertyModel.valueHandle, + Qt.vector3d(valueX, valueY, valueZ), true); + } + } + } + + Component { + id: sliderPropertyComponent + + HandlerPropertySlider { + readonly property var actionProperty: parent ? _parentView.property : null + + sliderMin: actionProperty ? actionProperty.min : 0 + sliderMax: actionProperty ? actionProperty.max : 100 + intSlider: actionProperty ? actionProperty.type === DataModelDataType.Long : false + value: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined ? propertyModel.value : sliderMin + label: parent ? parent.label : "" + + // We don't need to care about preview for action sliders + onCommitValue: _parentView.setArgumentValue(propertyModel.valueHandle, desiredValue) + } + } + + Component { + id: comboPropertyComponent + + HandlerPropertyCombo { + readonly property var actionProperty: parent ? _parentView.property : null + property var propertyValue: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined + ? propertyModel.value : "" + + label: parent ? parent.label : "" + comboModel: actionProperty ? actionProperty.possibleValues : null + + onValueChanged: _parentView.setArgumentValue(propertyModel.valueHandle, value) + onPropertyValueChanged: currentIndex = find(propertyValue) + } + } + + Component { + id: booleanComponent + + HandlerGenericCheckbox { + label: parent ? parent.label : "" + checked: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined ? propertyModel.value : false + + onClicked: { + _parentView.setArgumentValue(propertyModel.valueHandle, !checked) + } + } + } + + Component { + id: colorBox + + HandlerGenericColor { + readonly property var propValue: propertyModel && !_parentView.propertyValueInvalid + ? propertyModel.value : undefined + + label: parent ? parent.label : "" + color: "black" + onColorSelected: { + color = selectedColor; + _parentView.setArgumentValue(propertyModel.valueHandle, selectedColor); + } + onPreviewColorSelected: color = selectedColor + onPropValueChanged: { + color = propValue ? Qt.rgba(propValue.x, propValue.y, propValue.z, 1) + : "black"; + } + } + } + + Component { + id: genericTextComponent + + HandlerGenericText { + label: parent ? parent.label : "" + value: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined ? propertyModel.value : "" + onEditingFinished: _parentView.setArgumentValue(propertyModel.valueHandle, value) + } + } + + Component { + id: floatPropertyComponent + + HandlerGenericFloat { + label: parent ? parent.label : "" + value: propertyModel && !_parentView.propertyValueInvalid + && propertyModel.value !== undefined + ? Number(propertyModel.value).toFixed(numberOfDecimal) : 0 + + onEditingFinished: _parentView.setArgumentValue(propertyModel.valueHandle, desiredValue) + } + } + + Loader { + readonly property string label: qsTr("New Value") + readonly property var actionProperty: _parentView.property + + Layout.fillWidth: true + + onLoaded: { + _tabOrderHandler.clear(); + if (item.tabItem1 !== undefined) { + _tabOrderHandler.addItem(0, item.tabItem1) + if (item.tabItem2 !== undefined) { + _tabOrderHandler.addItem(0, item.tabItem2) + if (item.tabItem3 !== undefined) + _tabOrderHandler.addItem(0, item.tabItem3) + } + } + } + + sourceComponent: { + // KDAB_TODO Handle additionaltype + switch (actionProperty.type) { + case DataModelDataType.Float: + switch (actionProperty.additionalType) { + case AdditionalMetaDataType.FontSize: + return fontSizeComponent; + case AdditionalMetaDataType.Range: + return sliderPropertyComponent; + default: + return floatPropertyComponent; + } + case DataModelDataType.Long: + return sliderPropertyComponent; + case DataModelDataType.Float3: + switch (actionProperty.additionalType) { + case AdditionalMetaDataType.None: + case AdditionalMetaDataType.Rotation: + return xyzPropertyComponent; + default: + console.warn("KDAB_TODO implement property handler for additional " + + "typeDataModelDataType.Float3: ", actionProperty.additionalType); + return xyzPropertyComponent; + } + case DataModelDataType.Float4: + if (actionProperty.additionalType === AdditionalMetaDataType.Color) + return colorBox; + break; + + case DataModelDataType.String: + switch (actionProperty.additionalType) { + case AdditionalMetaDataType.StringList: + return comboPropertyComponent; + case AdditionalMetaDataType.MultiLine: + return multiLineComponent; + case AdditionalMetaDataType.Font: + return comboPropertyComponent; + case AdditionalMetaDataType.Import: + case AdditionalMetaDataType.Renderable: + case AdditionalMetaDataType.String: + return genericTextComponent; + default: + console.warn("KDAB_TODO implement property handler for additional type: ", + actionProperty.additionalType) + return null; + } + case DataModelDataType.Bool: + return booleanComponent; + case DataModelDataType.None: + return null; + default: console.warn("KDAB_TODO implement property handler for type: ", + actionProperty.type) + + } + return null; + } + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseSlider.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseSlider.qml new file mode 100644 index 00000000..7019dff2 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseSlider.qml @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +/* +* Use for: Opacity, Edge Tesselation Value, Inner Tesselation Value ... +* For the latter two set sliderMax to 64 +*/ + +Row { + id: root + + property real value: 0 // This is the value coming from backend + property alias desiredValue: slider.value // This is value adjusted by user + property alias sliderMin: slider.from + property alias sliderMax: slider.to + property real sliderDecimals: -1 + property bool intSlider: false + property int decimalSlider: sliderDecimals >= 0 ? sliderDecimals + : Math.min(precision(slider.stepSize), 3) + property Item tabItem1: textField + + signal previewValue // Indicates desiredValue contains a preview value + signal commitValue // Indicates desiredValue contains a final value to be committed + + spacing: 5 + width: _valueWidth + + function doCommitValue() { + wheelCommitTimer.stop(); + if (rateLimiter.running) + rateLimiter.stop(); + textField.setTextFieldValue(); + root.commitValue(); + } + + // get the number of decimals in a float/double + function precision(a) { + if (!isFinite(a)) return 0; + var e = 1, p = 0; + while (Math.round(a * e) / e !== a) { e *= 10; p++; } + return p; + } + + onValueChanged: { + slider.value = value; + textField.setTextFieldValue(); + } + + Keys.onPressed: { + if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) { + event.accepted = true + var delta = 1.0; + if (intSlider) { + if (event.key === Qt.Key_Down) + delta = -delta; + slider.value = Number(slider.value + delta).toFixed(0); + } else { + if (event.modifiers === Qt.ControlModifier) + delta = 0.1; + else if (event.modifiers === Qt.ShiftModifier) + delta = 10.0; + if (event.key === Qt.Key_Down) + delta = -delta; + slider.value = Number(slider.value + delta).toFixed(doubleValidator.decimals); + } + wheelCommitTimer.stop(); + if (!rateLimiter.running) + rateLimiter.start(); + textField.setTextFieldValue(); + } + } + + Slider { + id: slider + + leftPadding: 0 + + background: Rectangle { + x: slider.leftPadding + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + implicitWidth: _valueWidth - textField.width - 5 + implicitHeight: 6 + height: implicitHeight + radius: 2 + color: _studioColor2 + } + handle: Rectangle { + x: slider.leftPadding + slider.visualPosition * slider.availableWidth + y: slider.topPadding + slider.availableHeight / 2 - height / 2 + implicitWidth: 6 + implicitHeight: 12 + color: _studioColor3 + radius: 2 + } + + from: 0 + to: 100 + stepSize: root.intSlider ? 1 : sliderStepFromRange(slider.to, slider.from, 100) + snapMode: root.intSlider ? Slider.SnapAlways : Slider.NoSnap + + function sliderStepFromRange(top, bottom, steps) { + return ((top - bottom) / steps); + } + + onMoved: { + wheelCommitTimer.stop(); + if (!rateLimiter.running) + rateLimiter.start(); + textField.setTextFieldValue(); + } + + // onPressedChanged is triggered both mouse clicks and arrow keys, so adjusting with arrow + // keys will create undo point for each tick slider moves (even when holding the key down) + onPressedChanged: { + if (!pressed) + root.doCommitValue(); + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + + onWheel: { + var delta = (wheel.angleDelta.x != 0) ? wheel.angleDelta.x + : wheel.angleDelta.y; + + if (delta > 0) + slider.increase(); + else + slider.decrease(); + if (!rateLimiter.running) + rateLimiter.start(); + textField.setTextFieldValue(); + + // Leaving a transaction open can interfere with other editor functionality, + // so commit the wheel transaction after a brief delay + wheelCommitTimer.restart(); + } + Timer { + id: wheelCommitTimer + interval: 1000 + onTriggered: { + root.doCommitValue(); + } + } + } + } + + Timer { + id: rateLimiter + interval: 10 + onTriggered: { + root.previewValue(); + } + } + + DoubleValidator { + id: doubleValidator + + decimals: decimalSlider + bottom: slider.from + top: slider.to + locale: "C" + } + + IntValidator { + id: intValidator + + bottom: slider.from + top: slider.to + } + + StyledTextField { + id: textField + + height: _controlBaseHeight + width: 55 + text: intSlider ? slider.value.toFixed(0) : slider.value.toFixed(decimalSlider) + + validator: intSlider ? intValidator : doubleValidator + + onTextEdited: { + if (!intSlider && text.search(",")) { + text = text.replace(",",".") + } + if (intSlider) { + // handle limiting integer values when entered value is less than + // minimum value since IntValidator doesn't handle this + if (text.length >= sliderMin.toString().length && text < sliderMin) + text = text.substring(0, text.length - 1) + } + } + + onEditingFinished: { + if (text > sliderMax) + text = sliderMax + else if (text < sliderMin) + text = sliderMin + slider.value = text + root.doCommitValue(); + } + + function setTextFieldValue() { + text = intSlider ? slider.value.toFixed(0) : slider.value.toFixed(decimalSlider) + } + onActiveFocusChanged: { + if (!activeFocus) + setTextFieldValue() + } + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXY.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXY.qml new file mode 100644 index 00000000..4406703f --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXY.qml @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +// Used for: Tiling + +RowLayout { + id: root + + property alias valueX: textFieldX.text + property alias valueY: textFieldY.text + property int numberOfDecimal: 3 + property Item tabItem1: textFieldX + property Item tabItem2: textFieldY + + signal editingFinished + signal previewValueChanged + + spacing: 0 + + StyledLabel { + Layout.preferredWidth: 10 + text: qsTr("X") + color: _xAxisColor + } + + FloatTextField { + id: textFieldX + Layout.preferredWidth: (_valueWidth - 40) / 2 + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } + + Item { width: 20 } + + StyledLabel { + Layout.preferredWidth: 10 + text: qsTr("Y") + color: _yAxisColor + } + + FloatTextField { + id: textFieldY + Layout.preferredWidth: (_valueWidth - 40) / 2 + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXYZ.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXYZ.qml new file mode 100644 index 00000000..50440dba --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyBaseXYZ.qml @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +/* Use for: Position, Rotation, Scale, Pivot ... */ + +RowLayout { + id: root + + property alias valueX: textFieldX.text + property alias valueY: textFieldY.text + property alias valueZ: textFieldZ.text + property int numberOfDecimal: 3 + property Item tabItem1: textFieldX + property Item tabItem2: textFieldY + property Item tabItem3: textFieldZ + + signal editingFinished + signal previewValueChanged + transformOrigin: Item.Center + spacing: 0 + + StyledLabel { + Layout.preferredWidth: 10 + text: qsTr("X") + color: _xAxisColor + } + + FloatTextField { + id: textFieldX + Layout.preferredWidth: (_valueWidth - 50) / 3 + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } + + Item { width: 10 } + + StyledLabel { + Layout.preferredWidth: 10 + text: qsTr("Y") + color: _yAxisColor + } + + FloatTextField { + id: textFieldY + Layout.preferredWidth: (_valueWidth - 50) / 3 + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } + + Item { width: 10 } + + StyledLabel { + Layout.preferredWidth: 10 + text: qsTr("Z") + color: _zAxisColor + } + + FloatTextField { + id: textFieldZ + Layout.preferredWidth: (_valueWidth - 50) / 3 + decimalValue: numberOfDecimal + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } +} + diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyCombo.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyCombo.qml new file mode 100644 index 00000000..37e76aa9 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyCombo.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +/* Use for Tesselation mode, Horizontal alignment, Vertical alignment ... */ + +RowLayout { + id: root + + property alias label: labelField.text + property alias comboModel : comboBox.model + property alias comboTextRole: comboBox.textRole + property alias currentIndex: comboBox.currentIndex + property string value + + function find(text) { + return comboBox.find(text); + } + + StyledLabel { + id: labelField + text: qsTr("New Value") + } + + StyledComboBox { + id: comboBox + + Layout.fillWidth: true + onActivated: value = comboBox.textAt(currentIndex) + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertySlider.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertySlider.qml new file mode 100644 index 00000000..db6f88f2 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertySlider.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +/* +* Use for: Opacity, Edge Tesselation Value, Inner Tesselation Value ... +* For the latter two set sliderMax to 64 +*/ + +GridLayout { + id: root + + property alias value: propertySlider.value + property alias desiredValue: propertySlider.desiredValue + property alias sliderMin: propertySlider.sliderMin + property alias sliderMax: propertySlider.sliderMax + property alias label: labelItem.text + property alias intSlider: propertySlider.intSlider + property alias decimalSlider: propertySlider.decimalSlider + property alias tabItem1: propertySlider.tabItem1 + + signal previewValue + signal commitValue + + columns: 3 + + StyledLabel { + id: labelItem + text: label + } + + HandlerPropertyBaseSlider { + id: propertySlider + // proxy the signal upwards + onCommitValue: root.commitValue() + onPreviewValue: root.previewValue() + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyXYZ.qml b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyXYZ.qml new file mode 100644 index 00000000..6571f1d0 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/HandlerPropertyXYZ.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 +import "../controls" + +/* Use for: Position, Rotation, Scale, Pivot ... */ + +RowLayout { + id: root + + property alias valueX: propertyXYZ.valueX + property alias valueY: propertyXYZ.valueY + property alias valueZ: propertyXYZ.valueZ + property alias label: labelItem.text + property alias tabItem1: propertyXYZ.tabItem1 + property alias tabItem2: propertyXYZ.tabItem2 + property alias tabItem3: propertyXYZ.tabItem3 + property alias numberOfDecimal: propertyXYZ.numberOfDecimal + + signal editingFinished + signal previewValueChanged + + StyledLabel { + id: labelItem + Layout.alignment: Qt.AlignTop | Qt.AlignLeft + text: qsTr("New Value") + } + + HandlerPropertyBaseXYZ { + id: propertyXYZ + Layout.alignment: Qt.AlignRight + + onEditingFinished: root.editingFinished() + onPreviewValueChanged: root.previewValueChanged() + } +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.cpp new file mode 100644 index 00000000..8024204d --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "PropertyModel.h" + +#include "ClientDataModelBridge.h" +#include "Core.h" +#include "Doc.h" +#include "StudioApp.h" + +#include "Qt3DSDMActionCore.h" +#include "Qt3DSDMActionInfo.h" +#include "Qt3DSDMDataCore.h" +#include "Qt3DSDMMetaData.h" +#include "Qt3DSDMStudioSystem.h" + + +PropertyModel::PropertyModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +void PropertyModel::setAction(const qt3dsdm::Qt3DSDMActionHandle &action) +{ + beginResetModel(); + m_action = action; + m_valueHandle = 0; + m_nameHandle = 0; + m_properties.clear(); + + if (action.Valid()) { + auto doc = g_StudioApp.GetCore()->GetDoc(); + auto studioSystem = doc->GetStudioSystem(); + auto propertySystem = studioSystem->GetPropertySystem(); + auto bridge = studioSystem->GetClientDataModelBridge(); + + auto actionInfo = studioSystem->GetActionCore()->GetActionInfo(action); + + qt3dsdm::IMetaData &metaData(*studioSystem->GetActionMetaData()); + qt3dsdm::TMetaDataPropertyHandleList metaProperties; + const auto instance = bridge->GetInstance(actionInfo.m_Owner, actionInfo.m_TargetObject); + if (instance.Valid()) { + metaData.GetMetaDataProperties(instance, metaProperties); + + for (const auto &metaProperty: metaProperties) { + auto propertyMetaInfo = metaData.GetMetaDataPropertyInfo(metaProperty); + if (propertyMetaInfo->m_IsHidden == false) { + PropertyInfo property; + property.m_handle = propertyMetaInfo->m_Property; + property.m_name = QString::fromWCharArray( + propertySystem->GetFormalName(instance, + property.m_handle).wide_str()); + property.m_nameId = QString::fromWCharArray( + propertySystem->GetName(property.m_handle).wide_str()); + property.m_type = propertyMetaInfo->GetDataType(); + property.m_additionalType = propertyMetaInfo->GetAdditionalType(); + + const auto additionalMetaDataType = + propertySystem->GetAdditionalMetaDataType(instance, property.m_handle); + switch (additionalMetaDataType) { + case qt3dsdm::AdditionalMetaDataType::Range: { + const qt3dsdm::TMetaDataData &metaDataData = + propertySystem->GetAdditionalMetaDataData(instance, + property.m_handle); + qt3dsdm::SMetaDataRange minMax = + qt3dsdm::get<qt3dsdm::SMetaDataRange>(metaDataData); + property.m_min = minMax.m_min; + property.m_max = minMax.m_max; + break; + } + case qt3dsdm::AdditionalMetaDataType::StringList: { + const qt3dsdm::TMetaDataData &metaDataData = + propertySystem->GetAdditionalMetaDataData(instance, + property.m_handle); + auto values = qt3dsdm::get<qt3dsdm::TMetaDataStringList>(metaDataData); + QStringList possibleValues; + for (const auto &value: values) + possibleValues.append(QString::fromWCharArray(value.wide_str())); + property.m_possibleValues = possibleValues; + break; + } + case qt3dsdm::AdditionalMetaDataType::Font: { + std::vector<QString> fontNames; + doc->GetProjectFonts(fontNames); + QStringList possibleValues; + for (const auto &fontName: fontNames) + possibleValues.append(fontName); + property.m_possibleValues = possibleValues; + break; + } + default: + break; + } + // Skip Name, we don't want to allow changing that + // TODO: To be localized when/if we add support for metadata localization + if (property.m_name != QLatin1String("Name")) + m_properties.append(property); + } + } + } + } + endResetModel(); + + Q_EMIT valueHandleChanged(); +} + +void PropertyModel::setNameHandle(const qt3dsdm::Qt3DSDMHandlerArgHandle &handle) +{ + m_nameHandle = handle; +} + +void PropertyModel::setValueHandle(const qt3dsdm::Qt3DSDMHandlerArgHandle &handle) +{ + if (m_valueHandle != handle) { + m_valueHandle = handle; + updateDefaultPropertyIndex(); + updateValue(); + Q_EMIT valueHandleChanged(); + } +} + +int PropertyModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_properties.size(); +} + + +QVariant PropertyModel::data(const QModelIndex &index, int role) const +{ + if (!hasIndex(index.row(), index.column(), index.parent())) + return {}; + + const auto property = m_properties.at(index.row()); + + switch (role) + { + case NameRole: + return property.m_name; + case HandleRole: + return property.m_handle.GetHandleValue(); + default: + return {}; + } + + return QVariant(); +} + +QHash<int, QByteArray> PropertyModel::roleNames() const +{ + auto names = QAbstractItemModel::roleNames(); + names.insert(NameRole, "name"); + names.insert(HandleRole, "handle"); + + return names; +} + +PropertyInfo PropertyModel::property(int index) const +{ + if (index < 0 || index >= m_properties.size()) + return {}; + return m_properties[index]; +} + +int PropertyModel::valueHandle() const +{ + return m_valueHandle; +} + +QVariant PropertyModel::value() const +{ + return m_value; +} + +void PropertyModel::updateDefaultPropertyIndex() +{ + if (!m_nameHandle.Valid()) { + m_defaultPropertyIndex = -1; + Q_EMIT defaultPropertyIndexChanged(); + return; + } + + qt3dsdm::SValue sValue; + auto doc = g_StudioApp.GetCore()->GetDoc(); + auto studioSystem = doc->GetStudioSystem(); + studioSystem->GetActionCore()->GetHandlerArgumentValue(m_nameHandle, sValue); + + if (sValue.getType() != qt3dsdm::DataModelDataType::String) { + m_defaultPropertyIndex = -1; + Q_EMIT defaultPropertyIndexChanged(); + return; + } + + auto propertyName = qt3dsdm::get<QString>(sValue); + auto iter = std::find_if(m_properties.constBegin(), m_properties.constEnd(), + [&propertyName](const PropertyInfo &info) + { + return (info.m_nameId == propertyName); + }); + + auto index = std::distance(m_properties.constBegin(), iter); + + if (m_defaultPropertyIndex != index) { + m_defaultPropertyIndex = index; + Q_EMIT defaultPropertyIndexChanged(); + } +} + +int PropertyModel::defaultPropertyIndex() const +{ + return m_defaultPropertyIndex; +} + +void PropertyModel::updateValue() +{ + const auto oldValue = m_value; + if (!m_valueHandle.Valid()) { + m_value.clear(); + } else { + qt3dsdm::SValue sValue; + auto doc = g_StudioApp.GetCore()->GetDoc(); + auto studioSystem = doc->GetStudioSystem(); + studioSystem->GetActionCore()->GetHandlerArgumentValue(m_valueHandle, sValue); + m_value = sValue.toQVariant(); + } + if (oldValue != m_value) + Q_EMIT valueChanged(); +} diff --git a/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.h b/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.h new file mode 100644 index 00000000..a833752b --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Action/PropertyModel.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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$ +** +****************************************************************************/ + +#ifndef PROPERTYMODEL_H +#define PROPERTYMODEL_H + +#include <QAbstractListModel> + +#include "Qt3DSDMHandles.h" +#include "Qt3DSDMDataTypes.h" +#include "Qt3DSDMMetaDataTypes.h" + +struct PropertyInfo { + Q_PROPERTY(QString name MEMBER m_name CONSTANT FINAL) + Q_PROPERTY(float min MEMBER m_min CONSTANT FINAL) + Q_PROPERTY(float max MEMBER m_max CONSTANT FINAL) + Q_PROPERTY(qt3dsdm::DataModelDataType::Value type MEMBER m_type CONSTANT FINAL) + Q_PROPERTY(qt3dsdm::AdditionalMetaDataType::Value additionalType MEMBER m_additionalType CONSTANT FINAL) + Q_PROPERTY(QStringList possibleValues MEMBER m_possibleValues CONSTANT FINAL) + + qt3dsdm::Qt3DSDMPropertyHandle m_handle; + QString m_name; + QString m_nameId; + qt3dsdm::DataModelDataType::Value m_type; + qt3dsdm::AdditionalMetaDataType::Value m_additionalType; + QStringList m_possibleValues; + float m_min = 0.0f; + float m_max = 0.0f; + + Q_GADGET +}; + +class PropertyModel : public QAbstractListModel +{ + Q_PROPERTY(int valueHandle READ valueHandle NOTIFY valueHandleChanged FINAL) + Q_PROPERTY(QVariant value READ value NOTIFY valueChanged FINAL) + Q_PROPERTY(int defaultPropertyIndex READ defaultPropertyIndex NOTIFY defaultPropertyIndexChanged FINAL) + Q_OBJECT + +public: + explicit PropertyModel(QObject *parent = nullptr); + + enum Roles { + NameRole = Qt::DisplayRole, + HandleRole = Qt::UserRole + 1 + }; + + void setAction(const qt3dsdm::Qt3DSDMActionHandle &action); + void setNameHandle(const qt3dsdm::Qt3DSDMHandlerArgHandle &valueHandle); + void setValueHandle(const qt3dsdm::Qt3DSDMHandlerArgHandle &valueHandle); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash<int, QByteArray> roleNames() const override; + + PropertyInfo property(int index) const; + qt3dsdm::Qt3DSDMActionHandle action() const { return m_action; } + int valueHandle() const; + + QVariant value() const; + int defaultPropertyIndex() const; + +Q_SIGNALS: + void valueHandleChanged(); + void valueChanged(); + void defaultPropertyIndexChanged(); + +private: + void updateValue(); + void updateDefaultPropertyIndex(); + + QVector<PropertyInfo> m_properties; + qt3dsdm::Qt3DSDMActionHandle m_action; + qt3dsdm::Qt3DSDMHandlerArgHandle m_nameHandle; + qt3dsdm::Qt3DSDMHandlerArgHandle m_valueHandle; + int m_defaultPropertyIndex = -1; + QVariant m_value; +}; + +Q_DECLARE_METATYPE(PropertyInfo) + +#endif // PROPERTYMODEL_H |