diff options
Diffstat (limited to 'src')
122 files changed, 3312 insertions, 1138 deletions
diff --git a/src/Authoring/Client/Code/Core/Core/Core.cpp b/src/Authoring/Client/Code/Core/Core/Core.cpp index e4e31267..46e4c646 100644 --- a/src/Authoring/Client/Code/Core/Core/Core.cpp +++ b/src/Authoring/Client/Code/Core/Core/Core.cpp @@ -274,6 +274,7 @@ bool CCore::OnNewDocument(const QString &inDocument, bool isNewProject, bool sil // write a new presentation node to the uia file m_projectFile.addPresentationNode(theDocument); m_projectFile.updateDocPresentationId(); + m_projectFile.loadVariants(); m_projectFile.loadSubpresentationsAndDatainputs(g_StudioApp.m_subpresentations, g_StudioApp.m_dataInputDialogItems); g_StudioApp.getRenderer().RegisterSubpresentations(g_StudioApp.m_subpresentations); diff --git a/src/Authoring/Client/Code/Core/Doc/Doc.cpp b/src/Authoring/Client/Code/Core/Doc/Doc.cpp index 599e0306..a59529d5 100644 --- a/src/Authoring/Client/Code/Core/Doc/Doc.cpp +++ b/src/Authoring/Client/Code/Core/Doc/Doc.cpp @@ -719,18 +719,14 @@ qt3dsdm::Qt3DSDMInstanceHandle CDoc::GetFirstSelectableLayer() QVector<qt3dsdm::Qt3DSDMInstanceHandle> CDoc::getLayers() { - CClientDataModelBridge *bridge = m_StudioSystem->GetClientDataModelBridge(); - Q3DStudio::CGraphIterator layerIterator; GetAssetChildren(this, m_SceneInstance, layerIterator, OBJTYPE_LAYER); QVector<qt3dsdm::Qt3DSDMInstanceHandle> layerList; for (; !layerIterator.IsDone(); ++layerIterator) { - if (m_StudioSystem->IsInstance(layerIterator.GetCurrent()) - && !bridge->IsLockedAtAll(layerIterator.GetCurrent())) { + if (m_StudioSystem->IsInstance(layerIterator.GetCurrent())) layerList.append(layerIterator.GetCurrent()); - } } return layerList; @@ -2373,7 +2369,7 @@ std::shared_ptr<Q3DStudio::IComposerSerializer> CDoc::CreateSerializer() *theCoreSystem.GetActionCore(), *m_AssetGraph, *theFullSystem.GetSlideSystem(), *theFullSystem.GetActionSystem(), *theCoreSystem.GetSlideGraphCore(), theClientBridge.GetObjectDefinitions(), m_ImportFailedHandler, - *theCoreSystem.GetGuideSystem()); + *theCoreSystem.GetGuideSystem(), *theFullSystem.GetPropertySystem()); } std::shared_ptr<Q3DStudio::IComposerSerializer> CDoc::CreateTransactionlessSerializer() @@ -2390,9 +2386,9 @@ std::shared_ptr<Q3DStudio::IComposerSerializer> CDoc::CreateTransactionlessSeria *theCoreSystem.GetTransactionlessAnimationCore(), *theCoreSystem.GetTransactionlessActionCore(), *m_AssetGraph, *theFullSystem.GetSlideSystem(), *theFullSystem.GetActionSystem(), - *theCoreSystem.GetTransactionlessSlideGraphCore(), theClientBridge.GetObjectDefinitions(), - m_ImportFailedHandler, *theCoreSystem.GetGuideSystem()); - + *theCoreSystem.GetTransactionlessSlideGraphCore(), + theClientBridge.GetObjectDefinitions(), m_ImportFailedHandler, + *theCoreSystem.GetGuideSystem(), *theFullSystem.GetPropertySystem()); } std::shared_ptr<qt3dsdm::IDOMWriter> CDoc::CreateDOMWriter() diff --git a/src/Authoring/Client/Code/Core/Doc/Doc.h b/src/Authoring/Client/Code/Core/Doc/Doc.h index dad6b365..1cf64168 100644 --- a/src/Authoring/Client/Code/Core/Doc/Doc.h +++ b/src/Authoring/Client/Code/Core/Doc/Doc.h @@ -182,6 +182,11 @@ public: QString name; int type; QVector<ControlledItem> ctrldElems; + // As per QT3DS-2992 we currently need only a single key-value pair per datainput. + // For efficiency we use separate QStrings for both, as there is no need for more + // elaborate containers. + QString metaDataKey; + QString metaData; // Bindings in other subpresentations, of QMap format // QMultiMap<subpresentation_id, QPair<datatype, strict>>. diff --git a/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.cpp b/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.cpp index 3a1907b7..4307b310 100644 --- a/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.cpp +++ b/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.cpp @@ -335,6 +335,7 @@ struct SComposerSerializerImpl : public IComposerSerializer SComposerObjectDefinitions &m_ObjectDefinitions; qt3dsdm::IStringTable &m_StringTable; std::shared_ptr<Q3DStudio::IImportFailedHandler> m_ImportFailedHandler; + IPropertySystem &m_propertySystem; // The instances we have discovered when we are writing THandleToIdMap m_HandleToIdMap; @@ -390,7 +391,7 @@ struct SComposerSerializerImpl : public IComposerSerializer IActionSystem &inActionSystem, ISlideGraphCore &inSlideGraphCore, SComposerObjectDefinitions &inObjectDefinitions, std::shared_ptr<Q3DStudio::IImportFailedHandler> inFailedHandler, - IGuideSystem &inGuideSystem) + IGuideSystem &inGuideSystem, IPropertySystem &inPropSystem) : m_DataCore(inDataCore) , m_MetaData(inMetaData) , m_SlideCore(inSlideCore) @@ -404,6 +405,7 @@ struct SComposerSerializerImpl : public IComposerSerializer , m_ObjectDefinitions(inObjectDefinitions) , m_StringTable(inDataCore.GetStringTable()) , m_ImportFailedHandler(inFailedHandler) + , m_propertySystem(inPropSystem) , m_Foundation(Q3DStudio::Foundation::SStudioFoundation::Create()) , m_InputStreamFactory(Q3DStudio::IInputStreamFactory::Create()) , m_PreserveFileIds(true) @@ -942,8 +944,11 @@ struct SComposerSerializerImpl : public IComposerSerializer const pair<Qt3DSDMPropertyHandle, SValue> &theValue(inList[idx]); QString theName(m_DataCore.GetProperty(theValue.first).m_Name); WriteDataModelValue(theValue.second, theValueStr); - if (GetValueType(theValue.second) == DataModelDataType::String || theValueStr.size()) - inWriter.Att(theName, theValueStr); + if (GetValueType(theValue.second) == DataModelDataType::String || theValueStr.size()) { + // this property is saved under the <Graph> node + if (theName != QStringLiteral("variants")) + inWriter.Att(theName, theValueStr); + } } } @@ -1627,7 +1632,16 @@ struct SComposerSerializerImpl : public IComposerSerializer } IDOMWriter::Scope __instanceScope(inWriter, theType); - inWriter.Att(L"id", GetInstanceId(inInstance)); + inWriter.Att(QStringLiteral("id"), GetInstanceId(inInstance)); + + // for layers, save the variants property under the <Graph> + if (theType.getValue() == QStringLiteral("Layer")) { + auto prop = m_propertySystem.GetAggregateInstancePropertyByName( + inInstance, QStringLiteral("variants")); + SValue sVal; + if (m_propertySystem.GetInstancePropertyValue(inInstance, prop, sVal)) + inWriter.Att(QStringLiteral("variants"), get<QString>(sVal)); + } m_InstanceSet.insert(inInstance); @@ -2832,12 +2846,15 @@ struct SComposerSerializerImpl : public IComposerSerializer std::shared_ptr<IComposerSerializer> IComposerSerializer::CreateGraphSlideSerializer( IDataCore &inDataCore, IMetaData &inMetaData, ISlideCore &inSlideCore, IAnimationCore &inAnimationCore, IActionCore &inActionCore, CGraph &inAssetGraph, - ISlideSystem &inSlideSystem, IActionSystem &inActionSystem, ISlideGraphCore &inSlideGraphCore, + ISlideSystem &inSlideSystem, IActionSystem &inActionSystem, + ISlideGraphCore &inSlideGraphCore, SComposerObjectDefinitions &inObjectDefinitions, - std::shared_ptr<Q3DStudio::IImportFailedHandler> inFailedHandler, IGuideSystem &inGuideSystem) + std::shared_ptr<Q3DStudio::IImportFailedHandler> inFailedHandler, + IGuideSystem &inGuideSystem, IPropertySystem &inPropSystem) { - return std::shared_ptr<SComposerSerializerImpl>(new SComposerSerializerImpl( - inDataCore, inMetaData, inSlideCore, inAnimationCore, inActionCore, inAssetGraph, - inSlideSystem, inActionSystem, inSlideGraphCore, inObjectDefinitions, inFailedHandler, - inGuideSystem)); + return std::shared_ptr<SComposerSerializerImpl>( + new SComposerSerializerImpl( + inDataCore, inMetaData, inSlideCore, inAnimationCore, inActionCore, + inAssetGraph, inSlideSystem, inActionSystem, inSlideGraphCore, + inObjectDefinitions, inFailedHandler, inGuideSystem, inPropSystem)); } diff --git a/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.h b/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.h index 301a8144..1a18b262 100644 --- a/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.h +++ b/src/Authoring/Client/Code/Core/Doc/IComposerSerializer.h @@ -49,6 +49,7 @@ class IActionSystem; class ISlideGraphCore; class IGuideSystem; class SComposerObjectDefinitions; +class IPropertySystem; }; namespace Q3DStudio { @@ -102,13 +103,14 @@ public: friend class std::shared_ptr<IComposerSerializer>; static std::shared_ptr<IComposerSerializer> CreateGraphSlideSerializer( - qt3dsdm::IDataCore &inDataCore, qt3dsdm::IMetaData &inMetaData, qt3dsdm::ISlideCore &inSlideCore, - qt3dsdm::IAnimationCore &inAnimationCore, qt3dsdm::IActionCore &inActionCore, - CGraph &inAssetGraph, qt3dsdm::ISlideSystem &inSlideSystem, - qt3dsdm::IActionSystem &inActionSystem, qt3dsdm::ISlideGraphCore &inSlideGraphCore, - qt3dsdm::SComposerObjectDefinitions &inObjectDefinitions, - std::shared_ptr<Q3DStudio::IImportFailedHandler> inFailedHandler, - qt3dsdm::IGuideSystem &inGuideSystem); + qt3dsdm::IDataCore &inDataCore, qt3dsdm::IMetaData &inMetaData, + qt3dsdm::ISlideCore &inSlideCore, + qt3dsdm::IAnimationCore &inAnimationCore, qt3dsdm::IActionCore &inActionCore, + CGraph &inAssetGraph, qt3dsdm::ISlideSystem &inSlideSystem, + qt3dsdm::IActionSystem &inActionSystem, qt3dsdm::ISlideGraphCore &inSlideGraphCore, + qt3dsdm::SComposerObjectDefinitions &inObjectDefinitions, + std::shared_ptr<Q3DStudio::IImportFailedHandler> inFailedHandler, + qt3dsdm::IGuideSystem &inGuideSystem, qt3dsdm::IPropertySystem &inPropSystem); }; } diff --git a/src/Authoring/Client/Code/Core/Doc/IKeyframesManager.h b/src/Authoring/Client/Code/Core/Doc/IKeyframesManager.h index da45d8a7..df028c3f 100644 --- a/src/Authoring/Client/Code/Core/Doc/IKeyframesManager.h +++ b/src/Authoring/Client/Code/Core/Doc/IKeyframesManager.h @@ -28,21 +28,14 @@ ****************************************************************************/ #ifndef INCLUDED_IKEYFRAMES_MANAGER_H -#define INCLUDED_IKEYFRAMES_MANAGER_H 1 +#define INCLUDED_IKEYFRAMES_MANAGER_H -#pragma once - -//============================================================================= -/** - * Interface to manage keyframes related actions - */ -//============================================================================= class IKeyframesManager { public: virtual ~IKeyframesManager() {} - virtual bool HasSelectedKeyframes(bool inOnlyDynamic = false) = 0; + virtual bool HasSelectedKeyframes() = 0; virtual bool HasDynamicKeyframes() = 0; virtual bool CanPerformKeyframeCopy() = 0; virtual bool CanPerformKeyframePaste() = 0; @@ -50,9 +43,13 @@ public: virtual bool RemoveKeyframes(bool inPerformCopy) = 0; virtual void PasteKeyframes() = 0; virtual void SetKeyframeInterpolation() = 0; - virtual void SelectAllKeyframes() = 0; virtual void DeselectAllKeyframes() = 0; virtual void SetChangedKeyframes() = 0; + + virtual void SetKeyframeTime(long inTime) = 0; + virtual void SetKeyframesDynamic(bool inDynamic) = 0; + virtual void CommitChangedKeyframes() = 0; + virtual void RollbackChangedKeyframes() = 0; }; #endif // INCLUDED_IKEYFRAMES_MANAGER_H diff --git a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp index 810f9f3d..36d4956b 100644 --- a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp +++ b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.cpp @@ -83,6 +83,8 @@ static QColor s_timelinePlayheadLineColor; static QColor s_timelineFilterButtonSelectedColor; static QColor s_timelineFilterButtonHoveredColor; static QColor s_timelineRowCommentBgColor; +static QColor s_timelinePressedKeyframeColor; // pressed keyframe from multiple selection +static QColor s_invalidDataInputIndicatorColor; static int s_fontSize; static int s_controlBaseHeight; @@ -195,6 +197,9 @@ void CStudioPreferences::loadPreferences(const QString &filePath) s_timelineRowSubpColor = QColor("#e2ceff"); s_timelineRowSubpDescendantColor = QColor("#a263ff"); s_timelineRowCommentBgColor = QColor("#d0000000"); + s_timelinePressedKeyframeColor = QColor("#ffff00"); + + s_invalidDataInputIndicatorColor = QColor("#ff2121"); s_fontSize = 12; s_controlBaseHeight = 22; @@ -1053,6 +1058,16 @@ QColor CStudioPreferences::timelineRowCommentBgColor() return s_timelineRowCommentBgColor; } +QColor CStudioPreferences::timelinePressedKeyframeColor() +{ + return s_timelinePressedKeyframeColor; +} + +QColor CStudioPreferences::invalidDataInputIndicatorColor() +{ + return s_invalidDataInputIndicatorColor; +} + int CStudioPreferences::fontSize() { return s_fontSize; diff --git a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h index a138bd5c..ed9da243 100644 --- a/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h +++ b/src/Authoring/Client/Code/Core/Utility/StudioPreferences.h @@ -197,6 +197,9 @@ public: static QColor timelineRowSubpColor(); static QColor timelineRowSubpDescendantColor(); static QColor timelineRowCommentBgColor(); + static QColor timelinePressedKeyframeColor(); + + static QColor invalidDataInputIndicatorColor(); static int fontSize(); static int controlBaseHeight(); diff --git a/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.cpp b/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.cpp index 83e9384b..e3f3e97b 100644 --- a/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.cpp +++ b/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.cpp @@ -316,6 +316,7 @@ struct DataConstructor<SObjectRefType> #define QT3DS_PROPNAME_lightmapradiosity "lightmapradiosity" #define QT3DS_PROPNAME_lightmapshadow "lightmapshadow" #define QT3DS_PROPNAME_controlledproperty "controlledproperty" +#define QT3DS_PROPNAME_variants "variants" QString ComposerObjectTypes::Convert(ComposerObjectTypes::Enum inType) { @@ -337,7 +338,7 @@ ComposerObjectTypes::Enum ComposerObjectTypes::Convert(const QString &inType) { #define HANDLE_COMPOSER_OBJECT_TYPE(name, propmacro) \ - if (inType == QLatin1String(QT3DS_PROPNAME_##name)) \ + if (inType == QLatin1String(QT3DS_PROPNAME_##name)) \ return ComposerObjectTypes::name; ITERATE_COMPOSER_OBJECT_TYPES #undef HANDLE_COMPOSER_OBJECT_TYPE @@ -372,7 +373,7 @@ ComposerPropertyNames::Enum ComposerPropertyNames::Convert(const QString &inType { #define HANDLE_COMPOSER_PROPERTY_DUPLICATE(name, memberName, type, defaultValue) #define HANDLE_COMPOSER_PROPERTY_NO_DEFAULT(name, memberName, type) \ - if (inType == QLatin1String(QT3DS_PROPNAME_##name)) \ + if (inType == QLatin1String(QT3DS_PROPNAME_##name)) \ return name; #define HANDLE_COMPOSER_PROPERTY(name, memberName, type, defaultValue) \ HANDLE_COMPOSER_PROPERTY_NO_DEFAULT(name, memberName, type) @@ -397,11 +398,11 @@ ComposerPropertyNames::Enum ComposerPropertyNames::Convert(const QString &inType #define HANDLE_COMPOSER_OBJECT_TYPE(name, propmacro) \ SComposerTypePropertyDefinition<ComposerObjectTypes::name>::SComposerTypePropertyDefinition( \ - IDataCore &inCore, Qt3DSDMInstanceHandle inInstance) \ - : reserved(false) propmacro \ - { \ - Q_UNUSED(inCore);\ - Q_UNUSED(inInstance);\ + IDataCore &inCore, Qt3DSDMInstanceHandle inInstance) \ + : reserved(false) propmacro \ + { \ + Q_UNUSED(inCore); \ + Q_UNUSED(inInstance); \ } ITERATE_COMPOSER_OBJECT_TYPES #undef HANDLE_COMPOSER_OBJECT_TYPE diff --git a/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.h b/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.h index 4d54ea07..172e53ab 100644 --- a/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.h +++ b/src/Authoring/QT3DSDM/Systems/Qt3DSDMComposerTypeDefinitions.h @@ -70,7 +70,7 @@ class IPropertySystem; HANDLE_COMPOSER_OBJECT_TYPE(Model, ITERATE_COMPOSER_MODEL_PROPERTIES) \ HANDLE_COMPOSER_OBJECT_TYPE(Light, ITERATE_COMPOSER_LIGHT_PROPERTIES) \ HANDLE_COMPOSER_OBJECT_TYPE(Camera, ITERATE_COMPOSER_CAMERA_PROPERTIES) \ - HANDLE_COMPOSER_OBJECT_TYPE(Component, ITERATE_COMPOSER_COMPONENT_PROPERTIES) \ + HANDLE_COMPOSER_OBJECT_TYPE(Component, ITERATE_COMPOSER_COMPONENT_PROPERTIES) \ HANDLE_COMPOSER_OBJECT_TYPE(Text, ITERATE_COMPOSER_TEXT_PROPERTIES) \ HANDLE_COMPOSER_OBJECT_TYPE(RenderPlugin, ITERATE_COMPOSER_NO_ADDITIONAL_PROPERTIES) \ HANDLE_COMPOSER_OBJECT_TYPE(Alias, ITERATE_COMPOSER_ALIAS_PROPERTIES) \ @@ -93,8 +93,8 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(importid, m_ImportId, TDataStrPtr, L"") \ HANDLE_COMPOSER_PROPERTY(importfile, m_ImportFile, TDataStrPtr, L"") \ HANDLE_COMPOSER_PROPERTY(fileid, m_FileId, TDataStrPtr, L"") \ - HANDLE_COMPOSER_PROPERTY(starttime, m_StartTime, qt3ds::QT3DSI32, 0) \ - HANDLE_COMPOSER_PROPERTY(endtime, m_EndTime, qt3ds::QT3DSI32, 10000) \ + HANDLE_COMPOSER_PROPERTY(starttime, m_StartTime, qt3ds::QT3DSI32, 0) \ + HANDLE_COMPOSER_PROPERTY(endtime, m_EndTime, qt3ds::QT3DSI32, 10000) \ HANDLE_COMPOSER_PROPERTY(eyeball, m_Eyeball, bool, true) \ HANDLE_COMPOSER_PROPERTY(shy, m_Shy, bool, false) \ HANDLE_COMPOSER_PROPERTY(locked, m_Locked, bool, false) \ @@ -118,12 +118,12 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(opacity, m_Opacity, float, 100.f) \ HANDLE_COMPOSER_PROPERTY(rotationorder, m_RotationOrder, TDataStrPtr, L"YXZ") \ HANDLE_COMPOSER_PROPERTY(orientation, m_Orientation, TDataStrPtr, L"Left Handed") \ - HANDLE_COMPOSER_PROPERTY(boneid, m_BoneId, qt3ds::QT3DSI32, 0) \ + HANDLE_COMPOSER_PROPERTY(boneid, m_BoneId, qt3ds::QT3DSI32, 0) \ HANDLE_COMPOSER_PROPERTY(ignoresparent, m_IgnoresParent, bool, false) \ HANDLE_COMPOSER_PROPERTY_DUPLICATE(controlledproperty, m_ControlledProperty, TDataStrPtr, L"") #define ITERATE_COMPOSER_MODEL_PROPERTIES \ - HANDLE_COMPOSER_PROPERTY(poseroot, m_PoseRoot, qt3ds::QT3DSI32, -1) \ + HANDLE_COMPOSER_PROPERTY(poseroot, m_PoseRoot, qt3ds::QT3DSI32, -1) \ HANDLE_COMPOSER_PROPERTY(tessellation, m_Tessellation, TDataStrPtr, L"None") \ HANDLE_COMPOSER_PROPERTY(edgetess, m_EdgeTess, float, 1.0) \ HANDLE_COMPOSER_PROPERTY(innertess, m_InnerTess, float, 1.0) \ @@ -219,7 +219,7 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(aodistance, m_AoDistance, float, 0) \ HANDLE_COMPOSER_PROPERTY(aosoftness, m_AoSoftness, float, 0) \ HANDLE_COMPOSER_PROPERTY(aobias, m_AoBias, float, 0) \ - HANDLE_COMPOSER_PROPERTY(aosamplerate, m_AoSamplerate, qt3ds::QT3DSI32, 1) \ + HANDLE_COMPOSER_PROPERTY(aosamplerate, m_AoSamplerate, qt3ds::QT3DSI32, 1) \ HANDLE_COMPOSER_PROPERTY(aodither, m_AoDither, bool, false) \ HANDLE_COMPOSER_PROPERTY(shadowstrength, m_ShadowStrength, float, 0) \ HANDLE_COMPOSER_PROPERTY(shadowdist, m_ShadowDist, float, 0) \ @@ -234,6 +234,7 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(probe2fade, m_Probe2Fade, float, 1) \ HANDLE_COMPOSER_PROPERTY(probe2window, m_Probe2Window, float, 1) \ HANDLE_COMPOSER_PROPERTY(probe2pos, m_Probe2Pos, float, 0.5f) \ + HANDLE_COMPOSER_PROPERTY(variants, m_variants, TDataStrPtr, L"") \ HANDLE_COMPOSER_PROPERTY_DUPLICATE(controlledproperty, m_ControlledProperty, TDataStrPtr, L"") #define ITERATE_COMPOSER_LIGHT_PROPERTIES \ @@ -250,7 +251,7 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(castshadow, m_CastShadow, bool, false) \ HANDLE_COMPOSER_PROPERTY(shdwbias, m_ShadowBias, float, 0.0f) \ HANDLE_COMPOSER_PROPERTY(shdwfactor, m_ShadowFactor, float, 5.0f) \ - HANDLE_COMPOSER_PROPERTY(shdwmapres, m_ShadowMapRes, qt3ds::QT3DSI32, 9) \ + HANDLE_COMPOSER_PROPERTY(shdwmapres, m_ShadowMapRes, qt3ds::QT3DSI32, 9) \ HANDLE_COMPOSER_PROPERTY(shdwmapfar, m_ShadowMapFar, float, 5000.0f) \ HANDLE_COMPOSER_PROPERTY(shdwmapfov, m_ShadowMapFov, float, 90.0f) \ HANDLE_COMPOSER_PROPERTY(shdwfilter, m_ShadowFilter, float, 35.0f) \ @@ -280,13 +281,13 @@ class IPropertySystem; HANDLE_COMPOSER_PROPERTY(dropshadow, m_DropShadow, bool, false) \ HANDLE_COMPOSER_PROPERTY(dropshadowstrength, m_DropShadowStrength, float, 80.f) \ HANDLE_COMPOSER_PROPERTY(dropshadowoffset, m_DropShadowOffset, float, 10.f) \ - HANDLE_COMPOSER_PROPERTY(dropshadowoffsetx, m_DropShadowOffsetX, float, 0.f) \ - HANDLE_COMPOSER_PROPERTY(dropshadowoffsety, m_DropShadowOffsetY, float, 0.f) \ - HANDLE_COMPOSER_PROPERTY(dropshadowhorzalign, m_DropShadowHorizontalAlignment, TDataStrPtr, L"Right") \ - HANDLE_COMPOSER_PROPERTY(dropshadowvertalign, m_DropShadowVerticalAlignment, TDataStrPtr, L"Bottom") \ + HANDLE_COMPOSER_PROPERTY(dropshadowoffsetx, m_DropShadowOffsetX, float, 0.f) \ + HANDLE_COMPOSER_PROPERTY(dropshadowoffsety, m_DropShadowOffsetY, float, 0.f) \ + HANDLE_COMPOSER_PROPERTY(dropshadowhorzalign, m_DropShadowHorizontalAlignment, TDataStrPtr, L"Right") \ + HANDLE_COMPOSER_PROPERTY(dropshadowvertalign, m_DropShadowVerticalAlignment, TDataStrPtr, L"Bottom") \ HANDLE_COMPOSER_PROPERTY(wordwrap, m_WordWrap, TDataStrPtr, L"WrapWord") \ HANDLE_COMPOSER_PROPERTY(boundingbox, m_BoundingBox, SFloat2, SFloat2(0, 0)) \ - HANDLE_COMPOSER_PROPERTY(elide, m_Elide, bool, false) \ + HANDLE_COMPOSER_PROPERTY(elide, m_Elide, TDataStrPtr, L"ElideNone") \ HANDLE_COMPOSER_PROPERTY(enableacceleratedfont, m_EnableAcceleratedFont, bool, false) \ HANDLE_COMPOSER_PROPERTY_DUPLICATE(controlledproperty, m_ControlledProperty, TDataStrPtr, L"") @@ -373,14 +374,14 @@ struct DataTypeToTypeMap bool force_compile_error; }; -#define QT3DSDM_DEFINE_TYPE_TO_DATA_TYPE(enumName, type) \ +#define QT3DSDM_DEFINE_TYPE_TO_DATA_TYPE(enumName, type) \ template <> \ struct TypeToDataTypeMap<type> \ { \ - static DataModelDataType::Value GetDataType() { return enumName; } \ + static DataModelDataType::Value GetDataType() { return enumName; } \ }; \ template <> \ - struct DataTypeToTypeMap<enumName> \ + struct DataTypeToTypeMap<enumName> \ { \ typedef type TDataType; \ }; @@ -449,9 +450,9 @@ struct SComposerTypePropertyDefinition template <> \ struct SComposerTypePropertyDefinition<ComposerObjectTypes::name> \ { \ - bool reserved; \ + bool reserved; \ propmacro SComposerTypePropertyDefinition(IDataCore &inCore, \ - Qt3DSDMInstanceHandle inInstance); \ + Qt3DSDMInstanceHandle inInstance); \ }; ITERATE_COMPOSER_OBJECT_TYPES @@ -933,7 +934,7 @@ public: IDataCore &inDataCore, IMetaData &inMetaData /*, ISlideCore& inSlideCore, IPropertySystem& inPropertySystem */); - /////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////// // RTTI API bool IsA(Qt3DSDMInstanceHandle inInstance, ComposerObjectTypes::Enum inType); // Could easily return None, meaning we can't identify the object type. diff --git a/src/Authoring/Studio/Application/DataInputDlg.cpp b/src/Authoring/Studio/Application/DataInputDlg.cpp index 0a31424d..9bfec2a0 100644 --- a/src/Authoring/Studio/Application/DataInputDlg.cpp +++ b/src/Authoring/Studio/Application/DataInputDlg.cpp @@ -28,6 +28,7 @@ #include "DataInputDlg.h" #include "ui_DataInputDlg.h" +#include "Qt3DSMessageBox.h" #include <QtWidgets/qabstractbutton.h> #include <QtGui/qstandarditemmodel.h> @@ -43,6 +44,8 @@ CDataInputDlg::CDataInputDlg(CDataInputDialogItem **datainput, QStandardItemMode , m_type(0) , m_min(0.0) , m_max(10.0) + , m_metadataKey(m_dataInput->metaDataKey) + , m_metadata(m_dataInput->metaData) , m_acceptedTypes(acceptedTypes) { m_ui->setupUi(this); @@ -87,6 +90,10 @@ CDataInputDlg::CDataInputDlg(CDataInputDialogItem **datainput, QStandardItemMode this, &CDataInputDlg::onMaxChanged); connect(m_ui->lineEditInputName, &QLineEdit::textChanged, this, &CDataInputDlg::onNameChanged); connect(m_ui->lineEditEvaluation, &QLineEdit::textChanged, this, &CDataInputDlg::onTextChanged); + connect(m_ui->lineEditMetadata, &QLineEdit::textChanged, this, + &CDataInputDlg::onMetadataChanged); + connect(m_ui->lineEditMetadataKey, &QLineEdit::textChanged, this, + &CDataInputDlg::onMetadataKeyChanged); } CDataInputDlg::~CDataInputDlg() @@ -127,11 +134,20 @@ void CDataInputDlg::initDialog() m_ui->doubleSpinBoxMax->setValue(m_dataInput->maxValue); } + m_metadata = m_dataInput->metaData; + m_metadataKey = m_dataInput->metaDataKey; + m_ui->lineEditMetadata->setText(m_metadata); + m_ui->lineEditMetadataKey->setText(m_metadataKey); updateVisibility(m_dataInput->type); } void CDataInputDlg::accept() { + if (m_metadataKey.isEmpty() && !m_metadata.isEmpty()) { + Qt3DSMessageBox::Show(tr("Metadata Error"), tr("Metadata key cannot be empty."), + Qt3DSMessageBox::ICON_WARNING, false, this); + return; + } if (m_dataInput->name != m_name) m_dataInput->name = getUniqueId(m_name); @@ -145,6 +161,8 @@ void CDataInputDlg::accept() m_dataInput->valueString = m_text; } #endif + m_dataInput->metaData = m_metadata; + m_dataInput->metaDataKey = m_metadataKey; QDialog::accept(); } @@ -181,6 +199,22 @@ void CDataInputDlg::onTextChanged(const QString &text) m_text = text; } +void CDataInputDlg::onMetadataChanged(const QString &metadata) +{ + int cursorPos = m_ui->lineEditMetadata->cursorPosition(); + m_metadata = metadata; + m_ui->lineEditMetadata->setText(metadata); + m_ui->lineEditMetadata->setCursorPosition(cursorPos); +} + +void CDataInputDlg::onMetadataKeyChanged(const QString &metadataKey) +{ + int cursorPos = m_ui->lineEditMetadataKey->cursorPosition(); + m_metadataKey = metadataKey; + m_ui->lineEditMetadataKey->setText(metadataKey); + m_ui->lineEditMetadataKey->setCursorPosition(cursorPos); +} + QString CDataInputDlg::getUniqueId(const QString &id) { QString retval = QStringLiteral("%1").arg(id); diff --git a/src/Authoring/Studio/Application/DataInputDlg.h b/src/Authoring/Studio/Application/DataInputDlg.h index b560c7cc..49593cd4 100644 --- a/src/Authoring/Studio/Application/DataInputDlg.h +++ b/src/Authoring/Studio/Application/DataInputDlg.h @@ -99,6 +99,8 @@ private Q_SLOTS: void onMaxChanged(float max); void onNameChanged(const QString &name); void onTextChanged(const QString &text); + void onMetadataChanged(const QString &metadata); + void onMetadataKeyChanged(const QString &metadataKey); private: Ui::DataInputDlg *m_ui; @@ -109,6 +111,8 @@ private: float m_min; int m_type; QString m_text; + QString m_metadataKey; + QString m_metadata; QVector<EDataType> m_acceptedTypes; }; diff --git a/src/Authoring/Studio/Application/DataInputDlg.ui b/src/Authoring/Studio/Application/DataInputDlg.ui index b27218c4..982e2d4f 100644 --- a/src/Authoring/Studio/Application/DataInputDlg.ui +++ b/src/Authoring/Studio/Application/DataInputDlg.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>533</width> - <height>244</height> + <height>300</height> </rect> </property> <property name="windowTitle"> @@ -133,6 +133,32 @@ </spacer> </item> <item> + <widget class="QLabel" name="labelMetadataKey"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>24</height> + </size> + </property> + <property name="text"> + <string>Metadata Key</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="labelMetadata"> + <property name="minimumSize"> + <size> + <width>0</width> + <height>24</height> + </size> + </property> + <property name="text"> + <string>Metadata</string> + </property> + </widget> + </item> + <item> <widget class="QLabel" name="labelEvaluation"> <property name="minimumSize"> <size> @@ -261,6 +287,20 @@ </spacer> </item> <item> + <widget class="QLineEdit" name="lineEditMetadataKey"> + <property name="toolTip"> + <string>Key for accessing the metadata for this Data Input</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEditMetadata"> + <property name="toolTip"> + <string>Metadata associated with this Data Input</string> + </property> + </widget> + </item> + <item> <widget class="QLineEdit" name="lineEditEvaluation"> <property name="toolTip"> <string/> diff --git a/src/Authoring/Studio/Application/DataInputListDlg.cpp b/src/Authoring/Studio/Application/DataInputListDlg.cpp index 4ba31ca9..dbf8d5e5 100644 --- a/src/Authoring/Studio/Application/DataInputListDlg.cpp +++ b/src/Authoring/Studio/Application/DataInputListDlg.cpp @@ -141,7 +141,7 @@ void CDataInputListDlg::initDialog() m_ui->elementInfo->setFocusPolicy(Qt::NoFocus); m_ui->elementInfo->resizeColumnsToContents(); m_ui->elementInfo->horizontalHeader()->setStretchLastSection(true); - m_ui->elementInfo->horizontalHeader()->setMinimumSectionSize(125); + m_ui->elementInfo->horizontalHeader()->setMinimumSectionSize(140); m_ui->elementInfo->setModel(m_infoContents); m_ui->elementInfo->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); @@ -190,7 +190,7 @@ void CDataInputListDlg::updateContents() for (auto &it : qAsConst(m_dataInputs)) { dataInput.clear(); - int dataInputType = it->type; + EDataType dataInputType = (EDataType)it->type; if ((dataInputType == m_typeFilter || m_typeFilter == -1) && it->name.contains(m_searchString)){ @@ -235,6 +235,26 @@ void CDataInputListDlg::updateContents() // highlight datainputs that are in use if (it->ctrldElems.size() || it->externalPresBoundTypes.size()) dataInput.first()->setForeground(QBrush(CStudioPreferences::dataInputColor())); + + // warn if any datainputs have mismatching datatype with an icon after datatype + // indicator + static QString warning(tr("Data Input type is not matching with one " + "or several bound properties")); + for (const auto &ctrlElem : qAsConst(it->ctrldElems)) { + if (!CDataInputDlg::getAcceptedTypes(ctrlElem.dataType.first) + .contains(dataInputType)) { + dataInput[1]->setIcon(QIcon(":/images/warning.png")); + dataInput[1]->setToolTip(warning); + } + } + + for (const auto &extBoundType : qAsConst(it->externalPresBoundTypes)) { + if (!CDataInputDlg::getAcceptedTypes(extBoundType.first).contains(dataInputType)) { + dataInput[1]->setIcon(QIcon(":/images/warning.png")); + dataInput[1]->setToolTip(warning); + } + } + m_tableContents->appendRow(dataInput); } } @@ -260,6 +280,7 @@ void CDataInputListDlg::updateInfo() if (m_ui->tableView->selectionModel()->selectedRows(0).size() == 1) { for (auto allCtrldElemsIt = m_dataInputs[m_currentDataInputName]->ctrldElems.begin(); allCtrldElemsIt != m_dataInputs[m_currentDataInputName]->ctrldElems.end();) { + bool typeNotMatching = false; QStandardItem *item = new QStandardItem( g_StudioApp.GetCore()->GetDoc()->GetStudioSystem() ->GetClientDataModelBridge()->GetName( @@ -297,6 +318,13 @@ void CDataInputListDlg::updateInfo() } count++; + + // Check if there is a non-matching datatype binding with one or several + // properties for this element. + if (!CDataInputDlg::getAcceptedTypes(allCtrldElemsIt->dataType.first).contains( + (EDataType)(m_dataInputs[m_currentDataInputName]->type))) { + typeNotMatching = true; + } // Advance main iterator so that after the inner loop we end up // at the start of next instance's batch of controlleditems. allCtrldElemsIt++; @@ -310,6 +338,15 @@ void CDataInputListDlg::updateInfo() QStandardItem *item3 = new QStandardItem(propNames); item3->setToolTip(propNames); item3->setEditable(false); + + // Highlight the entire property name item if a non-match was found. + if (typeNotMatching) { + item3->setForeground( + QBrush(CStudioPreferences::invalidDataInputIndicatorColor())); + static QString warning(tr("\n\nData Input type is not matching with one or " + "several bound properties")); + item3->setToolTip(propNames + warning); + } m_infoContents->appendRow(QList<QStandardItem *>({item, item2, item3})); } } diff --git a/src/Authoring/Studio/Application/DurationEditDlg.cpp b/src/Authoring/Studio/Application/DurationEditDlg.cpp index 0d7c4899..f0707a56 100644 --- a/src/Authoring/Studio/Application/DurationEditDlg.cpp +++ b/src/Authoring/Studio/Application/DurationEditDlg.cpp @@ -29,33 +29,15 @@ #include "ui_DurationEditDlg.h" #include "DurationEditDlg.h" -#include "IDoc.h" -#include "Bindings/ITimelineKeyframesManager.h" - +#include "TimeEnums.h" #include <QtGui/qvalidator.h> -//============================================================================= -/** - * Constructor - */ -CDurationEditDlg::CDurationEditDlg(QWidget *pParent) - : QDialog(pParent) +CDurationEditDlg::CDurationEditDlg(QWidget *parent) + : QDialog(parent) , m_ui(new Ui::DurationEditDlg) - , m_Doc(nullptr) - , m_KeyframesManager(nullptr) - , m_Callback(nullptr) - , m_MaxTime(0) - , m_MaxTimeDisplay(0) - , m_MinTimeDisplay(0) - , m_InitialTimeStart(0) - , m_InitialTimeEnd(0) - , m_minStart(-1) - , m_secStart(-1) - , m_minEnd(-1) - , m_secEnd(-1) { m_ui->setupUi(this); - setAutoFillBackground(true); + setWindowFlag(Qt::WindowContextHelpButtonHint, false); // remove '?' from the dialog title bar QIntValidator *minValidator = new QIntValidator(this); minValidator->setRange(0, 9999); @@ -83,8 +65,6 @@ CDurationEditDlg::CDurationEditDlg(QWidget *pParent) this, &CDurationEditDlg::onEndTimeChanged); connect(m_ui->lineEditEndMilliseconds, &QLineEdit::textEdited, this, &CDurationEditDlg::onEndTimeChanged); - - window()->setFixedSize(size()); } CDurationEditDlg::~CDurationEditDlg() @@ -92,72 +72,50 @@ CDurationEditDlg::~CDurationEditDlg() delete m_ui; } -void CDurationEditDlg::setKeyframesManager(ITimelineKeyframesManager *inKeyframesManager) -{ - m_KeyframesManager = inKeyframesManager; -} - -//============================================================================= /** - * showDialog: Initializes and shows the Duration Edit Dialog Box. + * Initializes and shows the Duration Edit Dialog Box. * @param startTime is the initial start time, which will be shown when the time edit * dialog box pops up * @param endTime is the initial end time, which will be shown when the time edit * dialog box pops up - * @param inDoc this can be nullptr where its not applicable * @param inCallback is the target object for the callbacks */ -void CDurationEditDlg::showDialog(long startTime, long endTime, IDoc *inDoc, - ITimeChangeCallback *inCallback) +void CDurationEditDlg::showDialog(long startTime, long endTime, ITimeChangeCallback *inCallback) { - m_InitialTimeStart = startTime; - m_InitialTimeEnd = endTime; - m_Doc = inDoc; m_Callback = inCallback; - m_MinTimeDisplay = 0; - // if it is a Timebar, this will be adjusted, else this should be initialized to some value at - // least, for OverflowHandling to work correctly - m_MaxTimeDisplay = LONG_MAX; - - // 9999:59:999 converted to milliseconds - m_MaxTime = timeConversion(9999, CONVERT_MIN_TO_MSEC) - + timeConversion(59, CONVERT_SEC_TO_MSEC) + 999; - // Set initial values to dialog - formatTime(m_InitialTimeStart, true); - formatTime(m_InitialTimeEnd, false); + formatTime(startTime, true); + formatTime(endTime, false); - // Present the dialog exec(); } void CDurationEditDlg::formatTime(long inTime, bool startTime) { - long theTime = inTime; - long min = 0; - long sec = 0; - long msec = 0; + long mins = 0; + long secs = 0; + long mils = 0; - // Translates the m_initialTime (in milliseconds) into Minutes, Seconds and Milliseconds if (inTime != 0) { - min = timeConversion(theTime, CONVERT_MSEC_TO_MIN); - theTime = theTime - timeConversion(min, CONVERT_MIN_TO_MSEC); - sec = timeConversion(theTime, CONVERT_MSEC_TO_SEC); - theTime = theTime - timeConversion(sec, CONVERT_SEC_TO_MSEC); - msec = theTime; + mins = inTime % 3600000 / 60000; + secs = inTime % 60000 / 1000; + mils = inTime % 1000; } + // display milliseconds in 3 digits (5 -> 005) + QString milsStr = QString("%1").arg(mils, 3, 10, QChar('0')); + if (startTime) { - m_ui->lineEditMinutes->setText(QString::number(min)); - m_ui->lineEditSeconds->setText(QString::number(sec)); - m_ui->lineEditMilliseconds->setText(QString::number(msec)); + m_ui->lineEditMinutes->setText(QString::number(mins)); + m_ui->lineEditSeconds->setText(QString::number(secs)); + m_ui->lineEditMilliseconds->setText(milsStr); // Select the biggest non-zero unit - if (min > 0) { + if (mins > 0) { m_ui->lineEditMinutes->setFocus(); m_ui->lineEditMinutes->selectAll(); - } else if (sec > 0) { + } else if (secs > 0) { m_ui->lineEditSeconds->setFocus(); m_ui->lineEditSeconds->selectAll(); } else { @@ -165,17 +123,12 @@ void CDurationEditDlg::formatTime(long inTime, bool startTime) m_ui->lineEditMilliseconds->selectAll(); } } else { - m_ui->lineEditEndMinutes->setText(QString::number(min)); - m_ui->lineEditEndSeconds->setText(QString::number(sec)); - m_ui->lineEditEndMilliseconds->setText(QString::number(msec)); + m_ui->lineEditEndMinutes->setText(QString::number(mins)); + m_ui->lineEditEndSeconds->setText(QString::number(secs)); + m_ui->lineEditEndMilliseconds->setText(milsStr); } } -void CDurationEditDlg::showEvent(QShowEvent *ev) -{ - QDialog::showEvent(ev); -} - void CDurationEditDlg::accept() { m_Callback->Commit(); @@ -188,94 +141,6 @@ void CDurationEditDlg::reject() QDialog::reject(); } -long CDurationEditDlg::numberOfDigits(long number) -{ - long theNumberOfDigits = 0; - for (long theNumber = number; theNumber >= 1; theNumber = theNumber / 10) - theNumberOfDigits++; - return theNumberOfDigits; -} - -//============================================================================== -/** - * timeConversion: Converts inTime to the format specified by inFlags. - * For example: - * inTime = 5 sec inFlags = CONVERT_SEC_TO_MSEC - * The method will convert 5 sec into 5000 msec and - * returns the result. - * @param inTime stores the time to be converted. - * inOperationCode determines the type of time conversion to be done on the - * inTime. - * @return theResult stores the result of the time conversion. - */ -long CDurationEditDlg::timeConversion(long inTime, long inOperationCode) -{ - long theResult = 0; - switch (inOperationCode) { - case CONVERT_MIN_TO_MSEC: - theResult = inTime * 60 * 1000; - break; - case CONVERT_SEC_TO_MSEC: - theResult = inTime * 1000; - break; - case CONVERT_MSEC_TO_MIN: - theResult = inTime / (60 * 1000); - break; - case CONVERT_MSEC_TO_SEC: - theResult = inTime / 1000; - break; - } - return theResult; -} - -//============================================================================== -/** - * timeConversion: Takes in the time in mins:secs:msec and convert it to - * the corresponding time in msec. - * @param inMin stores the minutes to be converted. - * inSec stores the seconds to be converted. - * inMsec stores the milliseconds to be converted. - * inOperationCode determines the type of time conversion to be done on the - * inMin, inSec and inMsec. - * @return theResult stores the result of the time conversion. - */ -long CDurationEditDlg::timeConversion(long inMin, long inSec, long inMsec, long inOperationCode) -{ - long theResult = 0; - switch (inOperationCode) { - case CONVERT_TIME_TO_MSEC: - theResult = timeConversion(inMin, CONVERT_MIN_TO_MSEC) - + timeConversion(inSec, CONVERT_SEC_TO_MSEC) + inMsec; - break; - } - return theResult; -} - -//============================================================================== -/** - * timeConversion: Takes in the time in milliseconds and converts them - * to min : sec : msec. - * @param inTotalTime stores the total time in msec. - * ioMin stores the mins result of the time conversion - * ioSec stores the secs result of the time conversion - * ioMsec stores the msecs result of the time conversion - * inOperationCode determines the type of time conversion to be done on the - * inTotalTime. - */ -void CDurationEditDlg::timeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec, - long inOperationCode) -{ - switch (inOperationCode) { - case CONVERT_MSEC_TO_MIN_SEC_MSEC: - *ioMin = timeConversion(inTotalTime, CONVERT_MSEC_TO_MIN); - *ioSec = inTotalTime - timeConversion(*ioMin, CONVERT_MIN_TO_MSEC); - *ioSec = timeConversion(*ioSec, CONVERT_MSEC_TO_SEC); - *ioMsec = inTotalTime - timeConversion(*ioMin, CONVERT_MIN_TO_MSEC) - - timeConversion(*ioSec, CONVERT_SEC_TO_MSEC); - break; - } -} - void CDurationEditDlg::updateObjectTime(long inTime, bool startTime) { if (m_Callback) { @@ -288,59 +153,42 @@ void CDurationEditDlg::updateObjectTime(long inTime, bool startTime) void CDurationEditDlg::onStartTimeChanged() { - // Making sure that the start time is not greater than the end time, when - // the user modifies the start time of the timebar - m_MaxTimeDisplay = m_InitialTimeEnd; // the initial end time - m_MinTimeDisplay = 0; - long min = m_ui->lineEditMinutes->text().toInt(); long sec = m_ui->lineEditSeconds->text().toInt(); long msec = m_ui->lineEditMilliseconds->text().toInt(); - long theGoToTime = timeConversion(min, CONVERT_MIN_TO_MSEC) - + timeConversion(sec, CONVERT_SEC_TO_MSEC) + msec; + long theGoToTime = min * 60000 + sec * 1000 + msec; // Go to the time specified in the start time edit display updateObjectTime(theGoToTime, true); // If max number of digits reached in a number field, select the next - if (m_minStart != min && numberOfDigits(min) == 4) { + if (m_ui->lineEditMinutes->hasFocus() && min > 999) { m_ui->lineEditSeconds->setFocus(); m_ui->lineEditSeconds->selectAll(); - } else if (m_secStart != sec && numberOfDigits(sec) == 2) { + } else if (m_ui->lineEditSeconds->hasFocus() && sec > 9) { m_ui->lineEditMilliseconds->setFocus(); m_ui->lineEditMilliseconds->selectAll(); } - - m_minStart = min; - m_secStart = sec; } void CDurationEditDlg::onEndTimeChanged() { - // Let the end time of the time bar go as far as possible - m_MaxTimeDisplay = m_MaxTime; - m_MinTimeDisplay = m_InitialTimeStart; // the initial start time - long min = m_ui->lineEditEndMinutes->text().toInt(); long sec = m_ui->lineEditEndSeconds->text().toInt(); long msec = m_ui->lineEditEndMilliseconds->text().toInt(); - long theGoToTime = timeConversion(min, CONVERT_MIN_TO_MSEC) - + timeConversion(sec, CONVERT_SEC_TO_MSEC) + msec; + long theGoToTime = min * 60000 + sec * 1000 + msec; // Go to the time specified in the end time edit display updateObjectTime(theGoToTime, false); // If max number of digits reached in a number field, select the next - if (m_minEnd != min && numberOfDigits(min) == 4) { + if (m_ui->lineEditEndMinutes->hasFocus() && min > 999) { m_ui->lineEditEndSeconds->setFocus(); m_ui->lineEditEndSeconds->selectAll(); - } else if (m_secEnd != sec && numberOfDigits(sec) == 2) { + } else if (m_ui->lineEditEndSeconds->hasFocus() && sec > 9) { m_ui->lineEditEndMilliseconds->setFocus(); m_ui->lineEditEndMilliseconds->selectAll(); } - - m_minEnd = min; - m_secEnd = sec; } diff --git a/src/Authoring/Studio/Application/DurationEditDlg.h b/src/Authoring/Studio/Application/DurationEditDlg.h index 7630b02a..ddb42ec0 100644 --- a/src/Authoring/Studio/Application/DurationEditDlg.h +++ b/src/Authoring/Studio/Application/DurationEditDlg.h @@ -30,12 +30,9 @@ #ifndef DURATION_EDIT_DIALOG_H #define DURATION_EDIT_DIALOG_H -#include "TimeEnums.h" #include <QtWidgets/qdialog.h> -class CTimebarControl; class IDoc; -class ITimelineKeyframesManager; class ITimeChangeCallback { @@ -62,43 +59,23 @@ class CDurationEditDlg : public QDialog Q_OBJECT public: - CDurationEditDlg(QWidget *pParent = nullptr); // standard constructor - virtual ~CDurationEditDlg(); - void setKeyframesManager(ITimelineKeyframesManager *inKeyframeManager); - void showDialog(long startTime, long endTime, IDoc *inDoc, - ITimeChangeCallback *inCallback = nullptr); + CDurationEditDlg(QWidget *parent = nullptr); + ~CDurationEditDlg() override; + + void showDialog(long startTime, long endTime, ITimeChangeCallback *inCallback = nullptr); public Q_SLOTS: void accept() override; void reject() override; -protected: - void showEvent(QShowEvent *) override; - +private: void onStartTimeChanged(); void onEndTimeChanged(); void formatTime(long inTime, bool startTime); - long numberOfDigits(long number); - long timeConversion(long inTime, long inOperationCode); - long timeConversion(long inMin, long inSec, long inMsec, long inOperationCode); - void timeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec, - long inOperationCode); void updateObjectTime(long inTime, bool startTime); -protected: Ui::DurationEditDlg *m_ui; - IDoc *m_Doc; - ITimelineKeyframesManager *m_KeyframesManager; - ITimeChangeCallback *m_Callback; - long m_MaxTime; - long m_MaxTimeDisplay; - long m_MinTimeDisplay; - long m_InitialTimeStart; - long m_InitialTimeEnd; - int m_minStart; - int m_secStart; - int m_minEnd; - int m_secEnd; + ITimeChangeCallback *m_Callback = nullptr; }; #endif // DURATION_EDIT_DIALOG_H diff --git a/src/Authoring/Studio/Application/DurationEditDlg.ui b/src/Authoring/Studio/Application/DurationEditDlg.ui index 062a5051..4f6bb7de 100644 --- a/src/Authoring/Studio/Application/DurationEditDlg.ui +++ b/src/Authoring/Studio/Application/DurationEditDlg.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>300</width> + <width>343</width> <height>152</height> </rect> </property> @@ -14,6 +14,9 @@ <string>Set Timebar Start / End Time</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetFixedSize</enum> + </property> <property name="leftMargin"> <number>0</number> </property> @@ -70,7 +73,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditMinutes"/> + <widget class="QLineEdit" name="lineEditMinutes"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_2"> @@ -80,7 +90,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditSeconds"/> + <widget class="QLineEdit" name="lineEditSeconds"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_3"> @@ -90,7 +107,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditMilliseconds"/> + <widget class="QLineEdit" name="lineEditMilliseconds"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> </layout> </widget> @@ -124,7 +148,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditEndMinutes"/> + <widget class="QLineEdit" name="lineEditEndMinutes"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_7"> @@ -134,7 +165,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditEndSeconds"/> + <widget class="QLineEdit" name="lineEditEndSeconds"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_8"> @@ -144,7 +182,14 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditEndMilliseconds"/> + <widget class="QLineEdit" name="lineEditEndMilliseconds"> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> </layout> </widget> diff --git a/src/Authoring/Studio/Application/ProjectFile.cpp b/src/Authoring/Studio/Application/ProjectFile.cpp index a2792049..a4b5b9c0 100644 --- a/src/Authoring/Studio/Application/ProjectFile.cpp +++ b/src/Authoring/Studio/Application/ProjectFile.cpp @@ -43,6 +43,8 @@ #include <QtCore/qdiriterator.h> #include <QtCore/qsavefile.h> #include <QtCore/qtimer.h> +#include <QtCore/qrandom.h> +#include <QtWidgets/qmessagebox.h> ProjectFile::ProjectFile() { @@ -493,6 +495,8 @@ void ProjectFile::parseDataInputElem(const QDomElement &elem, item->valueString = elem.attribute(QStringLiteral("evaluator")); } #endif + item->metaDataKey = elem.attribute((QStringLiteral("metadatakey"))); + item->metaData = elem.attribute((QStringLiteral("metadata"))); dataInputs.insert(item->name, item); } } @@ -926,3 +930,676 @@ ProjectFile::getDiBindingtypesFromSubpresentations() const return map; } + +/** + * Load variants data to m_variantsDef + * + * @param filePath the file path to load the variants from. If empty, variants are loaded from the + * project file and replace m_variantsDef. If a filePath is specified, the loaded + * variants are merged with m_variantsDef + */ +void ProjectFile::loadVariants(const QString &filePath) +{ + if (!m_fileInfo.exists()) + return; + + bool isProj = filePath.isEmpty() || filePath == getProjectFilePath(); + QFile file(isProj ? getProjectFilePath() : filePath); + if (!file.open(QFile::Text | QFile::ReadOnly)) { + qWarning() << file.errorString(); + return; + } + + if (isProj) + m_variantsDef.clear(); + + QXmlStreamReader reader(&file); + reader.setNamespaceProcessing(false); + + VariantGroup *currentGroup = nullptr; + while (!reader.atEnd()) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("variantgroup")) { + QString groupId = reader.attributes().value(QLatin1String("id")).toString(); + QString groupColor = reader.attributes().value(QLatin1String("color")).toString(); + currentGroup = &m_variantsDef[groupId]; + currentGroup->m_color = groupColor; + } else if (reader.name() == QLatin1String("variant")) { + if (currentGroup) { + QString tagId = reader.attributes().value(QLatin1String("id")).toString(); + if (!currentGroup->m_tags.contains(tagId)) + currentGroup->m_tags.append(tagId); + } else { + qWarning() << "Error parsing variant tags."; + } + } else if (currentGroup) { + break; + } + } + } + + if (!isProj) { + // if loading variants from a file, update the uia + QDomDocument domDoc; + QSaveFile fileProj(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(fileProj, domDoc)) + return; + + QDomElement vElem = domDoc.documentElement().firstChildElement(QStringLiteral("variants")); + if (!vElem.isNull()) + domDoc.documentElement().removeChild(vElem); + + vElem = domDoc.createElement(QStringLiteral("variants")); + domDoc.documentElement().appendChild(vElem); + + const auto keys = m_variantsDef.keys(); + for (auto &g : keys) { + QDomElement gElem = domDoc.createElement(QStringLiteral("variantgroup")); + gElem.setAttribute(QStringLiteral("id"), g); + gElem.setAttribute(QStringLiteral("color"), m_variantsDef[g].m_color); + vElem.appendChild(gElem); + + for (auto &t : qAsConst(m_variantsDef[g].m_tags)) { + QDomElement tElem = domDoc.createElement(QStringLiteral("variant")); + tElem.setAttribute(QStringLiteral("id"), t); + gElem.appendChild(tElem); + } + } + + StudioUtils::commitDomDocumentSave(fileProj, domDoc); + } +} + +// Add a new tag to a variants group +void ProjectFile::addVariantTag(const QString &group, const QString &newTag) +{ + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomElement newTagElem = domDoc.createElement(QStringLiteral("variant")); + newTagElem.setAttribute(QStringLiteral("id"), newTag); + + QDomNodeList groupsElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")) + .elementsByTagName(QStringLiteral("variantgroup")); + + // update and save the uia + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == group) { + gElem.appendChild(newTagElem); + StudioUtils::commitDomDocumentSave(file, domDoc); + break; + } + } + + // update m_variantsDef + m_variantsDef[group].m_tags.append(newTag); +} + +// Add a new group, it is assumes that the new group name is unique +void ProjectFile::addVariantGroup(const QString &newGroup) +{ + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomElement variantsElem = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")); + + if (variantsElem.isNull()) { + QDomElement newVariantsElem = domDoc.createElement(QStringLiteral("variants")); + domDoc.documentElement().appendChild(newVariantsElem); + variantsElem = newVariantsElem; + } + + // generate random semi-bright color + int r = 0x555555 + QRandomGenerator::global()->generate() % 0x555555; // 0x555555 = 0xffffff / 3 + QString newColor = QLatin1Char('#') + QString::number(r, 16); + + QDomElement newGroupElem = domDoc.createElement(QStringLiteral("variantgroup")); + newGroupElem.setAttribute(QStringLiteral("id"), newGroup); + newGroupElem.setAttribute(QStringLiteral("color"), newColor); + variantsElem.appendChild(newGroupElem); + StudioUtils::commitDomDocumentSave(file, domDoc); + + // update m_variantsDef + VariantGroup g; + g.m_color = newColor; + m_variantsDef[newGroup] = g; +} + +void ProjectFile::renameVariantTag(const QString &group, const QString &oldTag, + const QString &newTag) +{ + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + // rename the tag in all uip files + QDomNodeList presElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("assets")) + .elementsByTagName(QStringLiteral("presentation")); + for (int i = 0; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + renameTagInUip(pPath, group, oldTag, newTag); + } + + // update and save the uia + QDomNodeList groupsElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")) + .elementsByTagName(QStringLiteral("variantgroup")); + + bool renamed = false; + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == group) { + QDomNodeList tagsElems = gElem.childNodes(); + for (int j = 0; j < tagsElems.count(); ++j) { + QDomElement tElem = tagsElems.at(j).toElement(); + if (tElem.attribute(QStringLiteral("id")) == oldTag) { + tElem.setAttribute(QStringLiteral("id"), newTag); + StudioUtils::commitDomDocumentSave(file, domDoc); + renamed = true; + break; + } + } + if (renamed) + break; + } + } + + // update the property + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + const auto layers = doc->getLayers(); + auto property = bridge->GetLayer().m_variants; + for (auto layer : layers) { + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, property, sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + QString oldGroupTagPair = QStringLiteral("%1:%2").arg(group).arg(oldTag); + if (propVal.contains(oldGroupTagPair)) { + propVal.replace(oldGroupTagPair, QStringLiteral("%1:%2").arg(group).arg(newTag)); + qt3dsdm::SValue sVal + = std::make_shared<qt3dsdm::CDataStr>(Q3DStudio::CString::fromQString(propVal)); + propertySystem->SetInstancePropertyValue(layer, property, sVal); + } + } + } + + // update m_variantsDef + for (auto &t : m_variantsDef[group].m_tags) { + if (t == oldTag) { + t = newTag; + renamed = true; + break; + } + } +} + +// rename a variant group, newGroup is assumed to be unique +void ProjectFile::renameVariantGroup(const QString &oldGroup, const QString &newGroup) +{ + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + // rename the group in all uip files + QDomNodeList presElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("assets")) + .elementsByTagName(QStringLiteral("presentation")); + for (int i = 0; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + renameGroupInUip(pPath, oldGroup, newGroup); + } + + // update and save the uia + QDomNodeList groupsElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")) + .elementsByTagName(QStringLiteral("variantgroup")); + + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == oldGroup) { + gElem.setAttribute(QStringLiteral("id"), newGroup); + StudioUtils::commitDomDocumentSave(file, domDoc); + break; + } + } + + // update the property + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + const auto layers = doc->getLayers(); + auto property = bridge->GetLayer().m_variants; + for (auto layer : layers) { + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, property, sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + QString oldGroupWithColon = QStringLiteral("%1:").arg(oldGroup); + if (propVal.contains(oldGroupWithColon)) { + propVal.replace(oldGroupWithColon, QStringLiteral("%1:").arg(newGroup)); + qt3dsdm::SValue sVal = std::make_shared<qt3dsdm::CDataStr>( + Q3DStudio::CString::fromQString(propVal)); + propertySystem->SetInstancePropertyValue(layer, property, sVal); + } + } + } + + // update m_variantsDef + m_variantsDef[newGroup] = m_variantsDef[oldGroup]; + m_variantsDef.remove(oldGroup); +} + +void ProjectFile::deleteVariantGroup(const QString &group) +{ + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + // check if group is in use in other presentations in the porject + int inUseIdx = -1; // index of first presentation that has the group in-use + QDomNodeList presElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("assets")) + .elementsByTagName(QStringLiteral("presentation")); + for (int i = 0; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + if (pPath != doc->GetDocumentPath() && groupExistsInUip(pPath, group)) { + inUseIdx = i; + break; + } + } + + if (inUseIdx != -1) { + QMessageBox box; + box.setWindowTitle(tr("Group tags in use")); + box.setText(tr("Some tags in the Group '%1' are in use in the project, are you sure you" + " want to delete the group?").arg(group)); + box.setIcon(QMessageBox::Warning); + box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + box.setButtonText(QMessageBox::Yes, QStringLiteral("Delete")); + switch (box.exec()) { + case QMessageBox::Yes: + // delete the group from all uips that use it + for (int i = inUseIdx; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + if (pPath != doc->GetDocumentPath()) + deleteGroupFromUip(pPath, group); + } + break; + + default: + // abort deletion + return; + } + } + + // delete the group from current uip, if exists + deleteGroupFromUip(doc->GetDocumentPath(), group); + + // delete the group from the property (if set) + const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + const auto layers = doc->getLayers(); + auto property = bridge->GetLayer().m_variants; + for (auto layer : layers) { + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, property, sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + if (propVal.contains(QStringLiteral("%1:").arg(group))) { + // property has the deleted group, need to update it, else the deleted group + // will be saved the uip if the user saves the presentation. + QRegExp rgx(QStringLiteral("%1:\\w*,*|,%1:\\w*").arg(group)); + propVal.replace(rgx, {}); + qt3dsdm::SValue sVal = std::make_shared<qt3dsdm::CDataStr>( + Q3DStudio::CString::fromQString(propVal)); + propertySystem->SetInstancePropertyValue(layer, property, sVal); + } + } + } + + // update and save the uia + QDomElement variantsElem = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")); + QDomNodeList groupsElems = variantsElem.elementsByTagName(QStringLiteral("variantgroup")); + + bool deleted = false; + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == group) { + variantsElem.removeChild(gElem); + StudioUtils::commitDomDocumentSave(file, domDoc); + deleted = true; + break; + } + } + + // update m_variantsDef + m_variantsDef.remove(group); +} + +void ProjectFile::changeVariantGroupColor(const QString &group, const QString &newColor) +{ + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + // update and save the uia + QDomNodeList groupsElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")) + .elementsByTagName(QStringLiteral("variantgroup")); + + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == group) { + gElem.setAttribute(QStringLiteral("color"), newColor); + StudioUtils::commitDomDocumentSave(file, domDoc); + break; + } + } + + // update m_variantsDef + m_variantsDef[group].m_color = newColor; +} + +bool ProjectFile::tagExistsInUip(const QString &src, const QString &group, const QString &tag) const +{ + QFile file(src); + if (!file.open(QFile::Text | QFile::ReadOnly)) { + qWarning() << file.errorString(); + return false; + } + + QXmlStreamReader reader(&file); + reader.setNamespaceProcessing(false); + + while (!reader.atEnd()) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("Layer") + && reader.attributes().hasAttribute(QLatin1String("variants"))) { + QStringRef v = reader.attributes().value(QLatin1String("variants")); + if (v.contains(group + QLatin1Char(':') + tag)) + return true; + } else if (reader.name() == QLatin1String("Logic")) { + break; + } + } + } + + return false; +} + +bool ProjectFile::groupExistsInUip(const QString &src, const QString &group) const +{ + QFile file(src); + if (!file.open(QFile::Text | QFile::ReadOnly)) { + qWarning() << file.errorString(); + return false; + } + + QXmlStreamReader reader(&file); + reader.setNamespaceProcessing(false); + + while (!reader.atEnd()) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("Layer") + && reader.attributes().hasAttribute(QLatin1String("variants"))) { + QStringRef v = reader.attributes().value(QLatin1String("variants")); + if (v.contains(group + QLatin1Char(':'))) + return true; + } else if (reader.name() == QLatin1String("Logic")) { + break; + } + } + } + + return false; +} + +// renames a tag (if exists) in all layers in a uip file +void ProjectFile::renameTagInUip(const QString &src, const QString &group, const QString &tag, + const QString &newName) +{ + QDomDocument domDoc; + QSaveFile file(src); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomNodeList layerElems = domDoc.documentElement() + .elementsByTagName(QStringLiteral("Layer")); + bool needSave = false; + for (int i = 0; i < layerElems.count(); ++i) { + QDomElement lElem = layerElems.at(i).toElement(); + if (lElem.hasAttribute(QStringLiteral("variants"))) { + QStringList tagPairs = lElem.attribute(QStringLiteral("variants")) + .split(QLatin1Char(',')); + QString tagFrom = group + QLatin1Char(':') + tag; + QString tagTo = group + QLatin1Char(':') + newName; + + if (tagPairs.contains(tagFrom)) { + tagPairs.replaceInStrings(tagFrom, tagTo); + lElem.setAttribute(QStringLiteral("variants"), tagPairs.join(QLatin1Char(','))); + needSave = true; + } + } + } + + if (needSave) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +void ProjectFile::renameGroupInUip(const QString &src, const QString &group, const QString &newName) +{ + QDomDocument domDoc; + QSaveFile file(src); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomNodeList layerElems = domDoc.documentElement() + .elementsByTagName(QStringLiteral("Layer")); + bool needSave = false; + for (int i = 0; i < layerElems.count(); ++i) { + QDomElement lElem = layerElems.at(i).toElement(); + if (lElem.hasAttribute(QStringLiteral("variants"))) { + QString variants = lElem.attribute(QStringLiteral("variants")); + if (variants.contains(group + QLatin1Char(':'))) { + variants.replace(group + QLatin1Char(':'), newName + QLatin1Char(':')); + lElem.setAttribute(QStringLiteral("variants"), variants); + needSave = true; + } + } + } + + if (needSave) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +// deletes a tag (if exists) from all layers in a uip file +void ProjectFile::deleteTagFromUip(const QString &src, const QString &group, const QString &tag) +{ + QDomDocument domDoc; + QSaveFile file(src); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomNodeList layerElems = domDoc.documentElement() + .elementsByTagName(QStringLiteral("Layer")); + bool needSave = false; + for (int i = 0; i < layerElems.count(); ++i) { + QDomElement lElem = layerElems.at(i).toElement(); + if (lElem.hasAttribute(QStringLiteral("variants"))) { + QStringList tagPairs = lElem.attribute(QStringLiteral("variants")) + .split(QLatin1Char(',')); + QString tagPair = group + QLatin1Char(':') + tag; + if (tagPairs.contains(tagPair)) { + tagPairs.removeOne(tagPair); + lElem.setAttribute(QStringLiteral("variants"), tagPairs.join(QLatin1Char(','))); + needSave = true; + } + } + } + + if (needSave) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +// deletes a group (if exists) from all layers in a uip file +void ProjectFile::deleteGroupFromUip(const QString &src, const QString &group) +{ + QDomDocument domDoc; + QSaveFile file(src); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + QDomNodeList layerElems = domDoc.documentElement() + .elementsByTagName(QStringLiteral("Layer")); + bool needSave = false; + QRegExp rgx(group + ":\\w*,*|," + group + ":\\w*"); + for (int i = 0; i < layerElems.count(); ++i) { + QDomElement lElem = layerElems.at(i).toElement(); + if (lElem.hasAttribute(QStringLiteral("variants"))) { + QString val = lElem.attribute(QStringLiteral("variants")); + if (rgx.indexIn(val) != -1) { + val.replace(rgx, ""); + lElem.setAttribute(QStringLiteral("variants"), val); + needSave = true; + } + } + } + + if (needSave) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +bool ProjectFile::isVariantGroupUnique(const QString &group) const +{ + return !m_variantsDef.contains(group); +} + +bool ProjectFile::isVariantTagUnique(const QString &group, const QString &tag) const +{ + if (!m_variantsDef.contains(group)) + return true; + + return !m_variantsDef[group].m_tags.contains(tag); +} + +void ProjectFile::deleteVariantTag(const QString &group, const QString &tag) +{ + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + QDomDocument domDoc; + QSaveFile file(getProjectFilePath()); + if (!StudioUtils::openDomDocumentSave(file, domDoc)) + return; + + // check if tag is in use in other presentations in the porject + int inUseIdx = -1; // list of presentations that has the tag in use + QDomNodeList presElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("assets")) + .elementsByTagName(QStringLiteral("presentation")); + for (int i = 0; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + if (pPath != doc->GetDocumentPath() + && tagExistsInUip(pPath, group, tag)) { + inUseIdx = i; + break; + } + } + + if (inUseIdx != -1) { + QMessageBox box; + box.setWindowTitle(tr("Tag in use")); + box.setText(tr("The tag '%1' is in use in another presentation, are you sure you want to" + " delete it?").arg(tag)); + box.setIcon(QMessageBox::Warning); + box.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + box.setButtonText(QMessageBox::Yes, QStringLiteral("Delete")); + switch (box.exec()) { + case QMessageBox::Yes: + // delete the tag from all uips that use it + for (int i = inUseIdx; i < presElems.count(); ++i) { + QString pPath = m_fileInfo.path() + QLatin1Char('/') + + presElems.at(i).toElement().attribute(QStringLiteral("src")); + if (pPath != doc->GetDocumentPath()) + deleteTagFromUip(pPath, group, tag); + } + break; + + default: + // abort deletion + return; + } + } + + // delete the tag from current doc, if exists + deleteTagFromUip(doc->GetDocumentPath(), group, tag); + + QDomNodeList groupsElems = domDoc.documentElement() + .firstChildElement(QStringLiteral("variants")) + .elementsByTagName(QStringLiteral("variantgroup")); + + // delete the tag from the property (if set) + const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + const auto layers = doc->getLayers(); + auto property = bridge->GetLayer().m_variants; + for (auto layer : layers) { + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, property, sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + if (propVal.contains(QStringLiteral("%1:%2").arg(group).arg(tag))) { + // property has the deleted tag, need to update it, else the deleted tag will be + // saved in the uip if the user saves the presentation. + QRegExp rgx(QStringLiteral("%1:%2,*|,%1:%2").arg(group).arg(tag)); + propVal.replace(rgx, {}); + qt3dsdm::SValue sVal = std::make_shared<qt3dsdm::CDataStr>( + Q3DStudio::CString::fromQString(propVal)); + propertySystem->SetInstancePropertyValue(layer, property, sVal); + } + } + } + + // update and save the uia + bool deleted = false; + for (int i = 0; i < groupsElems.count(); ++i) { + QDomElement gElem = groupsElems.at(i).toElement(); + if (gElem.attribute(QStringLiteral("id")) == group) { + QDomNodeList tagsElems = gElem.childNodes(); + for (int j = 0; j < tagsElems.count(); ++j) { + QDomElement tElem = tagsElems.at(j).toElement(); + if (tElem.attribute(QStringLiteral("id")) == tag) { + gElem.removeChild(tElem); + StudioUtils::commitDomDocumentSave(file, domDoc); + deleted = true; + break; + } + } + if (deleted) + break; + } + } + + // update m_variantsDef + m_variantsDef[group].m_tags.removeOne(tag); +} diff --git a/src/Authoring/Studio/Application/ProjectFile.h b/src/Authoring/Studio/Application/ProjectFile.h index 3e47da28..78ecc6d6 100644 --- a/src/Authoring/Studio/Application/ProjectFile.h +++ b/src/Authoring/Studio/Application/ProjectFile.h @@ -46,6 +46,11 @@ class ProjectFile : public QObject public: ProjectFile(); + struct VariantGroup { + QString m_color; + QStringList m_tags; + }; + void create(const QString &uiaPath); void ensureProjectFile(); void initProjectFile(const QString &presPath); @@ -81,6 +86,18 @@ public: void deletePresentationFile(const QString &filePath); void renameMaterial(const QString &oldName, const QString &newName); bool duplicatePresentation(const QString &oldPres, const QString &newPres); + void loadVariants(const QString &filePath = {}); + void addVariantTag(const QString &group, const QString &newTag); + void renameVariantTag(const QString &group, const QString &oldTag, const QString &newTag); + void deleteVariantTag(const QString &group, const QString &tag); + void addVariantGroup(const QString &newGroup); + void renameVariantGroup(const QString &oldGroup, const QString &newGroup); + void deleteVariantGroup(const QString &group); + void changeVariantGroupColor(const QString &group, const QString &newColor); + bool isVariantGroupUnique(const QString &group) const; + bool isVariantTagUnique(const QString &group, const QString &tag) const; + + QHash<QString, VariantGroup> variantsDef() const { return m_variantsDef; } Q_SIGNALS: void presentationIdChanged(const QString &path, const QString &id); @@ -88,9 +105,17 @@ Q_SIGNALS: private: QString ensureUniquePresentationId(const QString &id) const; + bool tagExistsInUip(const QString &src, const QString &group, const QString &tag) const; + bool groupExistsInUip(const QString &src, const QString &group) const; + void deleteTagFromUip(const QString &src, const QString &group, const QString &tag); + void deleteGroupFromUip(const QString &src, const QString &group); + void renameTagInUip(const QString &src, const QString &group, const QString &tag, + const QString &newName); + void renameGroupInUip(const QString &src, const QString &group, const QString &newName); QFileInfo m_fileInfo; // uia file info QString m_initialPresentation; + QHash<QString, VariantGroup> m_variantsDef; // definition of variants }; #endif // PROJECTFILE_H diff --git a/src/Authoring/Studio/Application/StudioApp.cpp b/src/Authoring/Studio/Application/StudioApp.cpp index d6e589a4..c0fa435e 100644 --- a/src/Authoring/Studio/Application/StudioApp.cpp +++ b/src/Authoring/Studio/Application/StudioApp.cpp @@ -84,11 +84,15 @@ int main(int argc, char *argv[]) bool isOpenGLES = false; QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #if !defined(Q_OS_MACOS) QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); #endif SharedTools::QtSingleApplication guiApp(QStringLiteral("Qt3DStudio"), argc, argv); + // Fix for uia and uip file attribute random ordering (see QTBUG-8158) + qSetGlobalQHashSeed(1720419); + #if defined(Q_OS_MACOS) QSurfaceFormat openGL33Format; openGL33Format.setRenderableType(QSurfaceFormat::OpenGL); @@ -1726,6 +1730,8 @@ bool CStudioApp::OnLoadDocument(const QString &inDocument, bool inShowStartupDia m_core->getProjectFile().updateDocPresentationId(); m_core->getProjectFile().loadSubpresentationsAndDatainputs(m_subpresentations, m_dataInputDialogItems); + m_core->getProjectFile().loadVariants(); + GetViews()->getMainFrame()->getSlideView()->refreshVariants(); getRenderer().RegisterSubpresentations(m_subpresentations); m_authorZoom = false; @@ -1786,6 +1792,13 @@ void CStudioApp::saveDataInputsToProjectFile() diNode.setAttribute(QStringLiteral("evaluator"), item->valueString); } #endif + // Let's allow storing key even if actual metadata is empty, as we + // do not know how the user code is going to interpret metadata contents. + if (!item->metaDataKey.isEmpty()) { + diNode.setAttribute(QStringLiteral("metadatakey"), item->metaDataKey); + if (!item->metaData.isEmpty()) + diNode.setAttribute(QStringLiteral("metadata"), item->metaData); + } assetsNode.appendChild(diNode); } StudioUtils::commitDomDocumentSave(file, doc); diff --git a/src/Authoring/Studio/Application/TimeEditDlg.cpp b/src/Authoring/Studio/Application/TimeEditDlg.cpp index 499d6d57..eb9d0324 100644 --- a/src/Authoring/Studio/Application/TimeEditDlg.cpp +++ b/src/Authoring/Studio/Application/TimeEditDlg.cpp @@ -29,24 +29,17 @@ #include "ui_TimeEditDlg.h" #include "TimeEditDlg.h" +#include "KeyframeManager.h" #include "IDoc.h" -#include "Bindings/ITimelineKeyframesManager.h" - +#include "TimeEnums.h" #include <QtGui/qvalidator.h> -CTimeEditDlg::CTimeEditDlg(QWidget *pParent) - : QDialog(pParent) - , m_ui(new Ui::TimeEditDlg) - , m_Doc(nullptr) - , m_KeyframesManager(nullptr) - , m_InitialTime(0) - , m_ObjectAssociation(0) - , m_OffsetFromInitialTime(0) - , m_min(-1) - , m_sec(-1) +CTimeEditDlg::CTimeEditDlg(KeyframeManager *keyframeManager) + : m_ui(new Ui::TimeEditDlg) + , m_keyframeManager(keyframeManager) { m_ui->setupUi(this); - setAutoFillBackground(true); + setWindowFlag(Qt::WindowContextHelpButtonHint, false); // remove '?' from the dialog title bar QIntValidator *minValidator = new QIntValidator(this); minValidator->setRange(0, 9999); @@ -61,8 +54,6 @@ CTimeEditDlg::CTimeEditDlg(QWidget *pParent) connect(m_ui->lineEditMinutes, &QLineEdit::textEdited, this, &CTimeEditDlg::onTimeChanged); connect(m_ui->lineEditSeconds, &QLineEdit::textEdited, this, &CTimeEditDlg::onTimeChanged); connect(m_ui->lineEditMilliseconds, &QLineEdit::textEdited, this, &CTimeEditDlg::onTimeChanged); - - window()->setFixedSize(size()); } CTimeEditDlg::~CTimeEditDlg() @@ -70,57 +61,48 @@ CTimeEditDlg::~CTimeEditDlg() delete m_ui; } -void CTimeEditDlg::setKeyframesManager(ITimelineKeyframesManager *inKeyframesManager) -{ - m_KeyframesManager = inKeyframesManager; -} - -//============================================================================= /** - * showDialog: Initializes and shows the Time Edit Dialog Box. - * @param inTime is the initial time, which will be shown when the time edit - * dialog box pops up + * Initializes and shows the dialog + * @param inTime the initial time which will be shown when the dialog pops up * @param inDoc this can be nullptr where its not applicable - * @param inObjectAssociation is the identifier for that identifies the object - * associated with the time edit dialog - * (e.g. playhead, keyframe) + * @param inObjectAssociation the identifier for the object associated with the dialog (playhead + * or keyframe) */ void CTimeEditDlg::showDialog(long inTime, IDoc *inDoc, long inObjectAssociation) { - m_InitialTime = inTime; - m_ObjectAssociation = inObjectAssociation; + m_initialTime = inTime; + m_objectAssociation = inObjectAssociation; m_Doc = inDoc; // Set initial values to dialog - formatTime(m_InitialTime); + formatTime(m_initialTime); exec(); } void CTimeEditDlg::formatTime(long inTime) { - long theTime = inTime; - long min = 0; - long sec = 0; - long msec = 0; + long mins = 0; + long secs = 0; + long mils = 0; - // Translates the m_initialTime (in milliseconds) into Minutes, Seconds and Milliseconds if (inTime != 0) { - min = timeConversion(theTime, CONVERT_MSEC_TO_MIN); - theTime = theTime - timeConversion(min, CONVERT_MIN_TO_MSEC); - sec = timeConversion(theTime, CONVERT_MSEC_TO_SEC); - theTime = theTime - timeConversion(sec, CONVERT_SEC_TO_MSEC); - msec = theTime; + mins = inTime % 3600000 / 60000; + secs = inTime % 60000 / 1000; + mils = inTime % 1000; } - m_ui->lineEditMinutes->setText(QString::number(min)); - m_ui->lineEditSeconds->setText(QString::number(sec)); - m_ui->lineEditMilliseconds->setText(QString::number(msec)); + + // display milliseconds in 3 digits (5 -> 005) + QString milsStr = QString("%1").arg(mils, 3, 10, QChar('0')); + m_ui->lineEditMinutes->setText(QString::number(mins)); + m_ui->lineEditSeconds->setText(QString::number(secs)); + m_ui->lineEditMilliseconds->setText(milsStr); // Select the biggest non-zero unit - if (min > 0) { + if (mins > 0) { m_ui->lineEditMinutes->setFocus(); m_ui->lineEditMinutes->selectAll(); - } else if (sec > 0) { + } else if (secs > 0) { m_ui->lineEditSeconds->setFocus(); m_ui->lineEditSeconds->selectAll(); } else { @@ -129,22 +111,23 @@ void CTimeEditDlg::formatTime(long inTime) } } -void CTimeEditDlg::showEvent(QShowEvent *ev) +void CTimeEditDlg::showEvent(QShowEvent *e) { onInitDialog(); - QDialog::showEvent(ev); + QDialog::showEvent(e); } void CTimeEditDlg::onInitDialog() { QString title; // Display the window captions for the correct object type - switch (m_ObjectAssociation) { + switch (m_objectAssociation) { case PLAYHEAD: title = QObject::tr("Go To Time"); break; case ASSETKEYFRAME: title = QObject::tr("Set Keyframe Time"); + Q_ASSERT(m_keyframeManager != nullptr); break; } setWindowTitle(title); @@ -154,11 +137,11 @@ void CTimeEditDlg::onInitDialog() void CTimeEditDlg::accept() { // Only commit here, cos dup keyframes will be deleted. - if (m_ObjectAssociation == ASSETKEYFRAME && m_Doc && m_KeyframesManager) { - if (m_OffsetFromInitialTime == 0) - m_KeyframesManager->RollbackChangedKeyframes(); + if (m_objectAssociation == ASSETKEYFRAME && m_Doc) { + if (m_endTime == m_initialTime) + m_keyframeManager->RollbackChangedKeyframes(); else - m_KeyframesManager->CommitChangedKeyframes(); + m_keyframeManager->CommitChangedKeyframes(); } QDialog::accept(); @@ -167,119 +150,26 @@ void CTimeEditDlg::accept() void CTimeEditDlg::reject() { // Only commit here, cos dup keyframes will be deleted. - if (m_ObjectAssociation == ASSETKEYFRAME && m_Doc && m_KeyframesManager) - m_KeyframesManager->RollbackChangedKeyframes(); + if (m_objectAssociation == ASSETKEYFRAME && m_Doc) + m_keyframeManager->RollbackChangedKeyframes(); QDialog::reject(); } -int CTimeEditDlg::numberOfDigits(long number) -{ - long theNumberOfDigits = 0; - for (long theNumber = number; theNumber >= 1; theNumber = theNumber / 10) - theNumberOfDigits++; - return theNumberOfDigits; -} - -//============================================================================== /** - * timeConversion: Converts inTime to the format specified by inFlags. - * For example: - * inTime = 5 sec inFlags = CONVERT_SEC_TO_MSEC - * The method will convert 5 sec into 5000 msec and - * returns the result. - * @param inTime stores the time to be converted. - * inOperationCode determines the type of time conversion to be done on the - * inTime. - * @return theResult stores the result of the time conversion. - */ -long CTimeEditDlg::timeConversion(long inTime, long inOperationCode) -{ - long theResult = 0; - switch (inOperationCode) { - case CONVERT_MIN_TO_MSEC: - theResult = inTime * 60 * 1000; - break; - case CONVERT_SEC_TO_MSEC: - theResult = inTime * 1000; - break; - case CONVERT_MSEC_TO_MIN: - theResult = inTime / (60 * 1000); - break; - case CONVERT_MSEC_TO_SEC: - theResult = inTime / 1000; - break; - } - return theResult; -} - -//============================================================================== -/** - * timeConversion: Takes in the time in mins:secs:msec and convert it to - * the corresponding time in msec. - * @param inMin stores the minutes to be converted. - * inSec stores the seconds to be converted. - * inMsec stores the milliseconds to be converted. - * inOperationCode determines the type of time conversion to be done on the - * inMin, inSec and inMsec. - * @return theResult stores the result of the time conversion. - */ -long CTimeEditDlg::timeConversion(long inMin, long inSec, long inMsec, long inOperationCode) -{ - long theResult = 0; - switch (inOperationCode) { - case CONVERT_TIME_TO_MSEC: - theResult = timeConversion(inMin, CONVERT_MIN_TO_MSEC) - + timeConversion(inSec, CONVERT_SEC_TO_MSEC) + inMsec; - break; - } - return theResult; -} - -//============================================================================== -/** - * timeConversion: Takes in the time in milliseconds and converts them - * to min : sec : msec. - * @param inTotalTime stores the total time in msec. - * ioMin stores the mins result of the time conversion - * ioSec stores the secs result of the time conversion - * ioMsec stores the msecs result of the time conversion - * inOperationCode determines the type of time conversion to be done on the - * inTotalTime. - */ -void CTimeEditDlg::timeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec, - long inOperationCode) -{ - switch (inOperationCode) { - case CONVERT_MSEC_TO_MIN_SEC_MSEC: - *ioMin = timeConversion(inTotalTime, CONVERT_MSEC_TO_MIN); - *ioSec = inTotalTime - timeConversion(*ioMin, CONVERT_MIN_TO_MSEC); - *ioSec = timeConversion(*ioSec, CONVERT_MSEC_TO_SEC); - *ioMsec = inTotalTime - timeConversion(*ioMin, CONVERT_MIN_TO_MSEC) - - timeConversion(*ioSec, CONVERT_SEC_TO_MSEC); - break; - } -} - -//============================================================================== -/** - * updateObjectTime: It updates the playhead or keyframe time according - * to the time displayed in the time edit dialogue. - * @param inTime is the time that will be updated. + * Updates the playhead or keyframe time according to the time displayed in the time edit dialogue. + * @param inTime the time that will be updated. */ void CTimeEditDlg::updateObjectTime(long inTime) { - long theDiff = 0; - switch (m_ObjectAssociation) { + switch (m_objectAssociation) { case PLAYHEAD: // Update the playhead time if (m_Doc) m_Doc->NotifyTimeChanged(inTime); break; case ASSETKEYFRAME: // Update the keyframe time if (m_Doc) { - theDiff = inTime - m_OffsetFromInitialTime - m_InitialTime; - m_OffsetFromInitialTime = m_OffsetFromInitialTime + theDiff; - if (theDiff != 0 && m_KeyframesManager) - m_KeyframesManager->OffsetSelectedKeyframes(theDiff); + m_endTime = inTime; + m_keyframeManager->moveSelectedKeyframes(inTime); } break; } @@ -291,21 +181,23 @@ void CTimeEditDlg::onTimeChanged() long sec = m_ui->lineEditSeconds->text().toInt(); long msec = m_ui->lineEditMilliseconds->text().toInt(); - long theGoToTime = timeConversion(min, CONVERT_MIN_TO_MSEC) - + timeConversion(sec, CONVERT_SEC_TO_MSEC) + msec; + long theGoToTime = min * 60000 + sec * 1000 + msec; + // make sure min keyframe time doesn't go below zero + long offset = m_keyframeManager->getPressedKeyframeOffset(); + if (theGoToTime - offset < 0) { + theGoToTime = offset; + formatTime(theGoToTime); + } // Go to the time specified in the time edit display updateObjectTime(theGoToTime); // If max number of digits reached in a number field, select the next - if (m_min != min && numberOfDigits(min) == 4) { + if (m_ui->lineEditMinutes->hasFocus() && min > 999) { m_ui->lineEditSeconds->setFocus(); m_ui->lineEditSeconds->selectAll(); - } else if (m_sec != sec && numberOfDigits(sec) == 2) { + } else if (m_ui->lineEditSeconds->hasFocus() && sec > 9) { m_ui->lineEditMilliseconds->setFocus(); m_ui->lineEditMilliseconds->selectAll(); } - - m_min = min; - m_sec = sec; } diff --git a/src/Authoring/Studio/Application/TimeEditDlg.h b/src/Authoring/Studio/Application/TimeEditDlg.h index 8d8ed08f..ef4f8d1a 100644 --- a/src/Authoring/Studio/Application/TimeEditDlg.h +++ b/src/Authoring/Studio/Application/TimeEditDlg.h @@ -30,12 +30,11 @@ #ifndef TIME_EDIT_DIALOG_H #define TIME_EDIT_DIALOG_H -#include "TimeEnums.h" #include <QtWidgets/qdialog.h> class CTimebarControl; class IDoc; -class ITimelineKeyframesManager; +class KeyframeManager; #ifdef QT_NAMESPACE using namespace QT_NAMESPACE; @@ -52,9 +51,8 @@ class CTimeEditDlg : public QDialog Q_OBJECT public: - CTimeEditDlg(QWidget *pParent = nullptr); // standard constructor - virtual ~CTimeEditDlg(); - void setKeyframesManager(ITimelineKeyframesManager *inKeyframeManager); + CTimeEditDlg(KeyframeManager *keyframeManager); + virtual ~CTimeEditDlg() override; void showDialog(long inTime, IDoc *inDoc, long inObjectAssociation); public Q_SLOTS: @@ -64,25 +62,18 @@ public Q_SLOTS: protected: void showEvent(QShowEvent *) override; +private: void onInitDialog(); void onTimeChanged(); void formatTime(long inTime); - int numberOfDigits(long number); - long timeConversion(long inTime, long inOperationCode); - long timeConversion(long inMin, long inSec, long inMsec, long inOperationCode); - void timeConversion(long inTotalTime, long *ioMin, long *ioSec, long *ioMsec, - long inOperationCode); void updateObjectTime(long inTime); -protected: - Ui::TimeEditDlg *m_ui; - IDoc *m_Doc; - ITimelineKeyframesManager *m_KeyframesManager; - long m_InitialTime; - long m_ObjectAssociation; - long m_OffsetFromInitialTime; - int m_min; - int m_sec; + Ui::TimeEditDlg *m_ui = nullptr; + IDoc *m_Doc = nullptr; + KeyframeManager *m_keyframeManager = nullptr; + long m_initialTime = 0; + long m_endTime = 0; + long m_objectAssociation = 0; }; #endif // TIME_EDIT_DIALOG_H diff --git a/src/Authoring/Studio/Application/TimeEditDlg.ui b/src/Authoring/Studio/Application/TimeEditDlg.ui index 6bcb11b3..54a8dbd7 100644 --- a/src/Authoring/Studio/Application/TimeEditDlg.ui +++ b/src/Authoring/Studio/Application/TimeEditDlg.ui @@ -6,14 +6,29 @@ <rect> <x>0</x> <y>0</y> - <width>300</width> - <height>114</height> + <width>343</width> + <height>119</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>535</width> + <height>119</height> + </size> + </property> <property name="windowTitle"> <string>Set Keyframe Time</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="sizeConstraint"> + <enum>QLayout::SetFixedSize</enum> + </property> <property name="leftMargin"> <number>0</number> </property> @@ -70,7 +85,20 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditMinutes"/> + <widget class="QLineEdit" name="lineEditMinutes"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_2"> @@ -80,7 +108,20 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditSeconds"/> + <widget class="QLineEdit" name="lineEditSeconds"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> <item> <widget class="QLabel" name="label_3"> @@ -90,7 +131,20 @@ </widget> </item> <item> - <widget class="QLineEdit" name="lineEditMilliseconds"/> + <widget class="QLineEdit" name="lineEditMilliseconds"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>50</width> + <height>16777215</height> + </size> + </property> + </widget> </item> </layout> </widget> @@ -152,7 +206,7 @@ <widget class="QLabel" name="label_4"> <property name="minimumSize"> <size> - <width>40</width> + <width>0</width> <height>0</height> </size> </property> @@ -174,7 +228,7 @@ <widget class="QLabel" name="label"> <property name="minimumSize"> <size> - <width>40</width> + <width>0</width> <height>0</height> </size> </property> diff --git a/src/Authoring/Studio/Info.plist b/src/Authoring/Studio/Info.plist index 6d81aee3..22709e99 100644 --- a/src/Authoring/Studio/Info.plist +++ b/src/Authoring/Studio/Info.plist @@ -54,7 +54,7 @@ </dict> </array> <key>NSHumanReadableCopyright</key> - <string>(C) 2018 The Qt Company Ltd</string> + <string>(C) 2019 The Qt Company Ltd</string> <key>CFBundleExecutable</key> <string>Qt3DStudio</string> <key>CFBundleIconFile</key> @@ -62,7 +62,9 @@ <key>CFBundleIdentifier</key> <string>org.qt-project.qt3dstudio</string> <key>CFBundleVersion</key> - <string>2.0</string> + <string>2.3.0</string> + <key>CFBundleShortVersionString</key> + <string>2.3</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleSignature</key> diff --git a/src/Authoring/Studio/MainFrm.cpp b/src/Authoring/Studio/MainFrm.cpp index 2fc6ad91..9bf14265 100644 --- a/src/Authoring/Studio/MainFrm.cpp +++ b/src/Authoring/Studio/MainFrm.cpp @@ -59,6 +59,7 @@ #include "ProjectView.h" #include "RowTree.h" #include "WidgetControl.h" +#include "SlideView.h" #include <QtGui/qevent.h> #include <QtGui/qdesktopservices.h> @@ -1901,6 +1902,12 @@ TimelineWidget *CMainFrame::getTimelineWidget() const return static_cast<TimelineWidget *>(control->getControl()); } +SlideView *CMainFrame::getSlideView() const +{ + return static_cast<SlideView *>(m_paletteManager->GetControl(CPaletteManager::CONTROLTYPE_SLIDE) + ->widget()); +} + CRecentItems *CMainFrame::GetRecentItems() { return m_recentItems.data(); diff --git a/src/Authoring/Studio/MainFrm.h b/src/Authoring/Studio/MainFrm.h index 275ea8c2..d81e9b12 100644 --- a/src/Authoring/Studio/MainFrm.h +++ b/src/Authoring/Studio/MainFrm.h @@ -56,6 +56,7 @@ class ITimelineTimebar; class RemoteDeploymentSender; class TimelineWidget; class CStudioPreferencesPropSheet; +class SlideView; #ifdef QT_NAMESPACE using namespace QT_NAMESPACE; @@ -234,6 +235,7 @@ public: void onCtrlNPressed(); TimelineWidget *getTimelineWidget() const; + SlideView *getSlideView() const; void EditPreferences(short inPageIndex); diff --git a/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp index 84e31178..3a1a008b 100644 --- a/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp +++ b/src/Authoring/Studio/Palettes/Inspector/ChooserModelBase.cpp @@ -231,6 +231,24 @@ void ChooserModelBase::expand(const QModelIndex &modelIndex) void ChooserModelBase::setRootPath(const QString &path) { + // Delete the old model. If the new project is in a totally different directory tree, not + // doing this will result in unexplicable crashes when trying to parse something that should + // not be parsed. + disconnect(m_model, &QAbstractItemModel::rowsInserted, + this, &ChooserModelBase::modelRowsInserted); + disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ChooserModelBase::modelRowsRemoved); + disconnect(m_model, &QAbstractItemModel::layoutChanged, + this, &ChooserModelBase::modelLayoutChanged); + delete m_model; + m_model = new QFileSystemModel(this); + connect(m_model, &QAbstractItemModel::rowsInserted, + this, &ChooserModelBase::modelRowsInserted); + connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ChooserModelBase::modelRowsRemoved); + connect(m_model, &QAbstractItemModel::layoutChanged, + this, &ChooserModelBase::modelLayoutChanged); + setRootIndex(m_model->setRootPath(path)); } diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp index 48cdfe1c..1e94d85a 100644 --- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp +++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.cpp @@ -62,6 +62,7 @@ #include "foundation/Qt3DSLogging.h" #include "Dialogs.h" #include "Dispatch.h" +#include "VariantsGroupModel.h" static QStringList renderableItems() { @@ -108,8 +109,9 @@ static std::pair<bool, bool> getSlideCharacteristics(qt3dsdm::Qt3DSDMInstanceHan return std::make_pair(hasNextSlide, hasPreviousSlide); } -InspectorControlModel::InspectorControlModel(QObject *parent) - : QAbstractListModel(parent) +InspectorControlModel::InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent) + : m_variantsModel(variantsModel) + , QAbstractListModel(parent) , m_UpdatableEditor(*g_StudioApp.GetCore()->GetDoc()) { m_modifiedProperty.first = 0; @@ -129,7 +131,7 @@ void InspectorControlModel::setInspectable(CInspectableBase *inInspectable) if (m_notifier.get() == nullptr) { m_notifier = signalProvider->ConnectInstancePropertyValue( - std::bind(&InspectorControlModel::notifyInstancePropertyValue, + std::bind(&InspectorControlModel::onPropertyChanged, this, std::placeholders::_1, std::placeholders::_2)); } if (m_slideNotifier.get() == nullptr) { @@ -172,14 +174,20 @@ CInspectableBase *getReferenceMaterialInspectable(CInspectableBase *inspectBase) return nullptr; } -void InspectorControlModel::notifyInstancePropertyValue(qt3dsdm::Qt3DSDMInstanceHandle inHandle, - qt3dsdm::Qt3DSDMPropertyHandle inProperty) +void InspectorControlModel::onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty) { auto doc = g_StudioApp.GetCore()->GetDoc(); - const auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem() - ->GetClientDataModelBridge(); - if (!bridge->IsSceneGraphInstance(inHandle)) + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + if (!bridge->IsSceneGraphInstance(inInstance)) return; + + if (inProperty == bridge->GetLayer().m_variants) { + // only update the variants model if its property changes + m_variantsModel->refresh(); + return; + } + bool changed = false; for (int row = 0; row < m_groupElements.count(); ++row) { auto group = m_groupElements[row]; @@ -192,7 +200,7 @@ void InspectorControlModel::notifyInstancePropertyValue(qt3dsdm::Qt3DSDMInstance imageInstance = doc->GetDocumentReader().GetImageInstanceForProperty( property->m_instance, property->m_property); } - if (property->m_property == inProperty || imageInstance == inHandle) { + if (property->m_property == inProperty || imageInstance == inInstance) { updatePropertyValue(property); changed = true; } @@ -621,6 +629,31 @@ QString InspectorControlModel::getDefaultMaterialString() const return QObject::tr("Default"); } +bool InspectorControlModel::isGroupCollapsed(int groupIdx) const +{ + const auto inspectable = dynamic_cast<Qt3DSDMInspectable *>(m_inspectableBase); + if (inspectable && groupIdx > -1 && groupIdx < m_groupElements.size()) { + auto instance = inspectable->GetGroupInstance(0); + + if (m_collapseMap.contains(instance)) + return m_collapseMap[instance].contains(groupIdx); + } + + return false; +} + +void InspectorControlModel::updateGroupCollapseState(int groupIdx, bool isCollapsed) +{ + const auto inspectable = dynamic_cast<Qt3DSDMInspectable *>(m_inspectableBase); + if (inspectable && groupIdx > -1 && groupIdx < m_groupElements.size()) { + auto instance = inspectable->GetGroupInstance(0); + if (isCollapsed) + m_collapseMap[instance][groupIdx] = true; + else + m_collapseMap[instance].remove(groupIdx); + } +} + void InspectorControlModel::updateFontValues(InspectorControlBase *element) const { // Find if there are any font items and update the values of those @@ -968,10 +1001,8 @@ bool InspectorControlModel::isTreeRebuildRequired(CInspectableBase* inspectBase) long theCount = m_inspectableBase->GetGroupCount(); auto refMaterial = getReferenceMaterial(inspectBase); - if (refMaterial != m_refMaterial) { - m_refMaterial = refMaterial; + if (refMaterial != m_refMaterial) return true; - } long refMaterialGroupCount = 0; if (refMaterial.Valid()) refMaterialGroupCount = 1; // Only the last group of the refMaterial is used @@ -1163,6 +1194,8 @@ void InspectorControlModel::rebuildTree() // Clean the old objects after reset is done so that qml will not freak out about null pointers for (int i = 0; i < deleteVector.count(); ++i) deleteVector[i]->deleteLater(); + + m_refMaterial = getReferenceMaterial(m_inspectableBase); } int InspectorControlModel::rowCount(const QModelIndex &parent) const @@ -1198,7 +1231,6 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c metaDataProvider->GetMetaDataProperty(instance, element->m_property)); } - bool skipEmits = false; switch (element->m_dataType) { case qt3dsdm::DataModelDataType::String: { @@ -1208,6 +1240,7 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c if (index != -1) stringValue = stringValue.mid(index + 1); } + element->m_value = stringValue; } // intentional fall-through for other String-derived datatypes case qt3dsdm::DataModelDataType::StringOrInt: @@ -1223,7 +1256,7 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c } auto slideSystem = studioSystem->GetSlideSystem(); - if (element->m_title == QStringLiteral("Play Mode")) { + if (element->m_title == QLatin1String("Play Mode")) { std::pair<bool, bool> slideData( getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(), *slideSystem)); @@ -1231,7 +1264,7 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c bool hasPreviousSlide(slideData.second); if (!hasNextSlide && !hasPreviousSlide) stringlist.removeAll("Play Through To..."); - } else if (element->m_title == QStringLiteral("Play Through To")) { + } else if (element->m_title == QLatin1String("Play Through To")) { // the code duplication is intentional as we may ask for slide characteristics // only if the property refers to slides std::pair<bool, bool> slideData( @@ -1274,7 +1307,8 @@ void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) c } element->m_value = QString(selectedIndex > 0 ? stringlist[selectedIndex] - : stringlist.first()).replace("|separator", ""); + : stringlist.first()).replace(QLatin1String("|separator"), + QString()); } element->m_values = stringlist; } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Import) { diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h index 13413bb4..300247c7 100644 --- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h +++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlModel.h @@ -42,6 +42,7 @@ class CInspectableBase; class Qt3DSDMInspectable; class SGuideInspectableImpl; +class VariantsGroupModel; namespace qt3dsdm { class ISignalConnection; @@ -107,7 +108,7 @@ class InspectorControlModel : public QAbstractListModel { Q_OBJECT public: - explicit InspectorControlModel(QObject *parent); + explicit InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent); ~InspectorControlModel() = default; enum Roles { @@ -153,6 +154,8 @@ public: Q_INVOKABLE bool isDefaultMaterial() const; Q_INVOKABLE void addMaterial(); Q_INVOKABLE void duplicateMaterial(); + Q_INVOKABLE bool isGroupCollapsed(int groupIdx) const; + Q_INVOKABLE void updateGroupCollapseState(int groupIdx, bool state); private: void onSlideRearranged(const qt3dsdm::Qt3DSDMSlideHandle &inMaster, int inOldIndex, @@ -168,7 +171,7 @@ private: } }; - mutable QVector<GroupInspectorControl> m_groupElements; + QVector<GroupInspectorControl> m_groupElements; CInspectableBase *m_inspectableBase = nullptr; SGuideInspectableImpl *m_guideInspectable = nullptr; @@ -197,6 +200,8 @@ private: qt3dsdm::SValue m_previouslyCommittedValue; + QHash<int, QHash<int, bool> > m_collapseMap; + QString getBasicMaterialString() const; QString getAnimatableMaterialString() const; QString getReferencedMaterialString() const; @@ -215,7 +220,8 @@ private: void updatePropertyValue(InspectorControlBase *element) const; void rebuildTree(); void refreshTree(); - void notifyInstancePropertyValue(qt3dsdm::Qt3DSDMInstanceHandle, qt3dsdm::Qt3DSDMPropertyHandle inProperty); + void onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty); void updateAnimateToggleState(InspectorControlBase *inItem); void updateControlledToggleState(InspectorControlBase *inItem) const; @@ -243,6 +249,8 @@ private: bool isGroupRebuildRequired(CInspectableBase *inspectable, int theIndex) const; static int handleToGuidePropIndex(int handle) { return handle - 1; } + + VariantsGroupModel *m_variantsModel = nullptr; }; #endif // INSPECTORCONTROLMODEL_H diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp index b6037011..5b2a69d9 100644 --- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp +++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.cpp @@ -57,6 +57,13 @@ #include "ProjectFile.h" #include "MaterialRefView.h" #include "BasicObjectsModel.h" +#include "Qt3DSDMSlides.h" +#include "VariantsGroupModel.h" +#include "VariantTagDialog.h" +#include "Views.h" +#include "MainFrm.h" +#include "SlideView.h" +#include "TimelineWidget.h" #include <QtCore/qtimer.h> #include <QtQml/qqmlcontext.h> @@ -68,7 +75,8 @@ InspectorControlView::InspectorControlView(const QSize &preferredSize, QWidget *parent) : QQuickWidget(parent), TabNavigable(), - m_inspectorControlModel(new InspectorControlModel(this)), + m_variantsGroupModel(new VariantsGroupModel(this)), + m_inspectorControlModel(new InspectorControlModel(m_variantsGroupModel, this)), m_meshChooserView(new MeshChooserView(this)), m_instance(0), m_handle(0), @@ -235,6 +243,7 @@ void InspectorControlView::initialize() CStudioPreferences::setQmlContextProperties(rootContext()); rootContext()->setContextProperty(QStringLiteral("_parentView"), this); rootContext()->setContextProperty(QStringLiteral("_inspectorModel"), m_inspectorControlModel); + rootContext()->setContextProperty(QStringLiteral("_variantsGroupModel"), m_variantsGroupModel); rootContext()->setContextProperty(QStringLiteral("_resDir"), StudioUtils::resourceImageUrl()); rootContext()->setContextProperty(QStringLiteral("_tabOrderHandler"), tabOrderHandler()); rootContext()->setContextProperty(QStringLiteral("_mouseHelper"), &m_mouseHelper); @@ -288,6 +297,8 @@ bool InspectorControlView::canLinkProperty(int instance, int handle) const && (type & (OBJTYPE_CUSTOMMATERIAL | OBJTYPE_MATERIAL | OBJTYPE_REFERENCEDMATERIAL))) { return false; } + if (doc->GetStudioSystem()->GetPropertySystem()->GetName(handle) == QStringLiteral("eyeball")) + return false; return doc->GetDocumentReader().CanPropertyBeLinked(instance, handle); } @@ -372,6 +383,17 @@ QString InspectorControlView::titleIcon() const return {}; } +bool InspectorControlView::isEditable(int handle) const +{ + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + if (doc->GetStudioSystem()->GetSlideSystem()->IsMasterSlide(doc->GetActiveSlide()) + && doc->GetStudioSystem()->GetPropertySystem()->GetName(handle) + == QStringLiteral("eyeball")) { + return false; + } + return true; +} + void InspectorControlView::OnSelectionSet(Q3DStudio::SSelectedValue inSelectable) { updateInspectable(g_StudioApp.GetInspectableFromSelectable(inSelectable)); @@ -394,6 +416,8 @@ void InspectorControlView::setInspectable(CInspectableBase *inInspectable) m_inspectorControlModel->setInspectable(inInspectable); Q_EMIT titleChanged(); + + m_variantsGroupModel->refresh(); } } @@ -434,6 +458,72 @@ void InspectorControlView::showContextMenu(int x, int y, int handle, int instanc m_handle = 0; } +// context menu for the variants tags +void InspectorControlView::showTagContextMenu(int x, int y, const QString &group, + const QString &tag) +{ + QMenu theContextMenu; + + auto actionRename = theContextMenu.addAction(QObject::tr("Rename Tag")); + connect(actionRename, &QAction::triggered, this, [&]() { + VariantTagDialog dlg(VariantTagDialog::RenameTag, group, tag); + if (dlg.exec() == QDialog::Accepted) { + g_StudioApp.GetCore()->getProjectFile().renameVariantTag(group, dlg.getNames().first, + dlg.getNames().second); + m_variantsGroupModel->refresh(); + } + }); + + auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Tag")); + connect(actionDelete, &QAction::triggered, this, [&]() { + g_StudioApp.GetCore()->getProjectFile().deleteVariantTag(group, tag); + g_StudioApp.GetViews()->getMainFrame()->getTimelineWidget()->refreshVariants(); + g_StudioApp.GetViews()->getMainFrame()->getSlideView()->refreshVariants(); + m_variantsGroupModel->refresh(); + }); + + theContextMenu.exec(mapToGlobal({x, y})); +} + +// context menu for the variants groups +void InspectorControlView::showGroupContextMenu(int x, int y, const QString &group) +{ + QMenu theContextMenu; + + ProjectFile &projectFile = g_StudioApp.GetCore()->getProjectFile(); + + auto actionRename = theContextMenu.addAction(QObject::tr("Rename Group")); + connect(actionRename, &QAction::triggered, this, [&]() { + VariantTagDialog dlg(VariantTagDialog::RenameGroup, {}, group); + if (dlg.exec() == QDialog::Accepted) { + projectFile.renameVariantGroup(dlg.getNames().first, dlg.getNames().second); + g_StudioApp.GetViews()->getMainFrame()->getTimelineWidget()->refreshVariants(); + m_variantsGroupModel->refresh(); + } + }); + + auto actionColor = theContextMenu.addAction(QObject::tr("Change Group Color")); + connect(actionColor, &QAction::triggered, this, [&]() { + const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef(); + QColor newColor = this->showColorDialog(variantsDef[group].m_color); + projectFile.changeVariantGroupColor(group, newColor.name()); + // no need to refresh variants in the timeline widget as it references the group color in + // the project file m_variants, and a redraw is triggered upon color selection dialog close. + g_StudioApp.GetViews()->getMainFrame()->getSlideView()->refreshVariants(); + m_variantsGroupModel->refresh(); + }); + + auto actionDelete = theContextMenu.addAction(QObject::tr("Delete Group")); + connect(actionDelete, &QAction::triggered, this, [&]() { + projectFile.deleteVariantGroup(group); + g_StudioApp.GetViews()->getMainFrame()->getTimelineWidget()->refreshVariants(); + g_StudioApp.GetViews()->getMainFrame()->getSlideView()->refreshVariants(); + m_variantsGroupModel->refresh(); + }); + + theContextMenu.exec(mapToGlobal({x, y})); +} + void InspectorControlView::toggleMasterLink() { Q3DStudio::ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(), diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h index 6cb7fd21..14936b57 100644 --- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h +++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.h @@ -39,6 +39,7 @@ #include "DataInputSelectView.h" class InspectorControlModel; +class VariantsGroupModel; class CInspectableBase; class ImageChooserView; class DataInputSelectView; @@ -72,6 +73,8 @@ public: QString titleIcon() const; Q_INVOKABLE void showContextMenu(int x, int y, int handle, int instance); + Q_INVOKABLE void showTagContextMenu(int x, int y, const QString &group, const QString &tag); + Q_INVOKABLE void showGroupContextMenu(int x, int y, const QString &group); Q_INVOKABLE QObject *showImageChooser(int handle, int instance, const QPoint &point); Q_INVOKABLE QObject *showFilesChooser(int handle, int instance, const QPoint &point); Q_INVOKABLE QObject *showMeshChooser(int handle, int instance, const QPoint &point); @@ -84,6 +87,7 @@ public: Q_INVOKABLE QString convertPathToProjectRoot(const QString &presentationPath); Q_INVOKABLE bool isRefMaterial(int instance) const; Q_INVOKABLE QString noneString() const; + Q_INVOKABLE bool isEditable(int handle) const; // IDataModelListener void OnBeginDataModelNotifications() override; @@ -125,6 +129,7 @@ private: std::vector<std::shared_ptr<qt3dsdm::ISignalConnection>> m_connections; QColor m_backgroundColor; + VariantsGroupModel *m_variantsGroupModel = nullptr; InspectorControlModel *m_inspectorControlModel = nullptr; CInspectableBase *m_inspectableBase = nullptr; QPointer<ImageChooserView> m_imageChooserView; diff --git a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml index 3677bc0e..ab0b8db3 100644 --- a/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml +++ b/src/Authoring/Studio/Palettes/Inspector/InspectorControlView.qml @@ -28,6 +28,8 @@ import QtQuick 2.8 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Extras 1.4 import Qt3DStudio 1.0 import "../controls" @@ -205,12 +207,45 @@ Rectangle { width: parent.width - x spacing: 4 - StyledLabel { - text: model.title + Rectangle { // group header + x: -10 + width: delegateItem.width + height: 25 + color: "#111111" + + StyledLabel { + x: 30 + text: model.title + anchors.verticalCenter: parent.verticalCenter + } + + Image { + id: collapseButton + x: 10 + anchors.verticalCenter: parent.verticalCenter + source: { + _resDir + (groupItems.visible ? "arrow_down.png" : "arrow.png") + } + } + + MouseArea { + id: collapseButtonMouseArea + anchors.fill: parent + onClicked: { + if (mouse.button === Qt.LeftButton) { + groupItems.visible = !groupItems.visible; + _inspectorModel.updateGroupCollapseState(indexOfThisDelegate, + !groupItems.visible) + } + } + } } Column { spacing: 4 + id: groupItems + + visible: !_inspectorModel.isGroupCollapsed(indexOfThisDelegate) Repeater { model: delegateItem.values @@ -237,6 +272,7 @@ Rectangle { RowLayout { id: groupDelegateItem spacing: 0 + enabled: _parentView.isEditable(modelData.handle) property alias loadedItem: loader.item @@ -252,6 +288,7 @@ Rectangle { ColumnLayout { // Property row and datainput control Layout.alignment: Qt.AlignTop + visible: modelData.title !== "variants" spacing: 0 RowLayout { // Property row Layout.alignment: Qt.AlignLeft @@ -281,7 +318,7 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton hoverEnabled: true - onClicked: { + onClicked: { if (mouse.button === Qt.LeftButton) { _inspectorModel.setPropertyAnimated( model.modelData.instance, @@ -389,6 +426,9 @@ Rectangle { opacity: enabled ? 1 : .5 Layout.alignment: Qt.AlignTop sourceComponent: { + if (modelData.title === "variants") + return variantTagsComponent; + const dataType = modelData.dataType; switch (dataType) { case DataModelDataType.Long: @@ -1064,4 +1104,173 @@ Rectangle { } } } + + Component { + id: variantTagsComponent + + Column { + width: root.width - 10 + spacing: 10 + + Row { + anchors.right: parent.right + anchors.rightMargin: 5 + spacing: 5 + + ToolButton { + id: importButton + text: qsTr("Import...") + width: 70 + height: 20 + + onClicked: { + _variantsGroupModel.importVariants() + } + } + + ToolButton { + id: exportButton + text: qsTr("Export...") + width: 70 + height: 20 + enabled: !_variantsGroupModel.variantsEmpty + + onClicked: { + _variantsGroupModel.exportVariants() + } + } + } + + Text { + text: qsTr("There are no variant tags yet. Click [+ Group] to add a new tags group and start adding tags.") + color: "#ffffff" + visible: _variantsGroupModel.variantsEmpty + } + + Repeater { + id: tagsRepeater + model: _variantsGroupModel + property int maxGroupLabelWidth; + + onItemAdded: { + // make all group labels have equal width as the widest one + if (index == 0) + maxGroupLabelWidth = 20; // min group label width + + if (item.groupLabelWidth > maxGroupLabelWidth) { + maxGroupLabelWidth = item.groupLabelWidth; + + if (maxGroupLabelWidth > 150) // max group label width + maxGroupLabelWidth = 150; + } + } + + Row { + id: variantTagsRow + spacing: 5 + + readonly property var tagsModel: model.tags + readonly property var groupModel: model + readonly property int groupLabelWidth: tLabel.implicitWidth + + Text { + id: tLabel + text: model.group + color: model.color + width: tagsRepeater.maxGroupLabelWidth; + elide: Text.ElideRight + anchors.top: parent.top + anchors.topMargin: 5 + + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.RightButton + onClicked: { + if (mouse.button === Qt.RightButton) { + const coords = mapToItem(root, mouse.x, mouse.y); + _parentView.showGroupContextMenu(coords.x, coords.y, model.group); + } + } + } + } + + Flow { + width: root.width - 110 + spacing: 5 + + Repeater { + model: tagsModel + + Loader { + readonly property var tagsModel: model + readonly property var grpModel: groupModel + sourceComponent: tagComponent + } + } + + ToolButton { + id: addTagButton + text: qsTr("+ Tag") + height: 25 + + onClicked: { + _variantsGroupModel.addNewTag(groupModel.group) + } + + } + } + } + } + + Item { width: 1; height: 5 } // vertical spacer + + ToolButton { + id: addGroupButton + text: qsTr("+ Group") + width: 60 + height: 25 + onClicked: { + _variantsGroupModel.addNewGroup() + } + } + + Item { width: 1; height: 5 } // vertical spacer + } + } + + Component { + id: tagComponent + + Rectangle { + property bool toggled: tagsModel.selected + property string grpColor: grpModel ? grpModel.color : "" + + width: Math.max(tLabel.width + 10, 60) + height: 25 + color: toggled ? grpColor : "#2e2f30" + border.color: "#959596" + + Text { + id: tLabel + anchors.centerIn: parent + text: tagsModel.tag + color: toggled ? "#ffffff" : "#959596" + } + + MouseArea { + anchors.fill: parent; + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: { + if (mouse.button === Qt.LeftButton) { + toggled = !toggled; + _variantsGroupModel.setTagState(grpModel.group, tagsModel.tag, toggled); + } else if (mouse.button === Qt.RightButton) { + const coords = mapToItem(root, mouse.x, mouse.y); + _parentView.showTagContextMenu(coords.x, coords.y, grpModel.group, + tagsModel.tag); + } + } + } + } + } } diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp index 0b6db847..41be5767 100644 --- a/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp +++ b/src/Authoring/Studio/Palettes/Inspector/ObjectBrowserView.cpp @@ -30,6 +30,10 @@ #include "ObjectListModel.h" #include "StudioPreferences.h" #include "StudioUtils.h" +#include "StudioApp.h" +#include "Core.h" +#include "Qt3DSDMStudioSystem.h" +#include "ClientDataModelBridge.h" #include <QtCore/qtimer.h> #include <QtQml/qqmlcontext.h> @@ -50,10 +54,29 @@ QAbstractItemModel *ObjectBrowserView::model() const void ObjectBrowserView::setModel(ObjectListModel *model) { - if (!m_model) { + if (!m_model) m_model = new FlatObjectListModel(model, this); - } m_model->setSourceModel(model); + + const auto doc = g_StudioApp.GetCore()->GetDoc(); + const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); + + // Remove "Scene.__Container" and "materials//Default" entries + QModelIndexList list = m_model->match(m_model->index(0, 0), + ObjectListModel::AbsolutePathRole, + QStringLiteral("Scene.") + + bridge->getMaterialContainerName(), 1, + Qt::MatchFlags(Qt::MatchWrap | Qt::MatchExactly + | Qt::MatchRecursive)); + list.append(m_model->match(m_model->index(0, 0), + ObjectListModel::NameRole, + QStringLiteral("materials/") + bridge->getDefaultMaterialName(), 1, + Qt::MatchFlags(Qt::MatchWrap | Qt::MatchExactly + | Qt::MatchRecursive))); + + for (int i = list.size(); i > 0; i--) + m_model->removeRow(list.at(i - 1).row(), m_model->index(0, 0)); + m_ownerInstance = 0; m_selection = -1; diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp index c1a5fd95..b91727f5 100644 --- a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp +++ b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.cpp @@ -407,6 +407,14 @@ int FlatObjectListModel::rowCount(const QModelIndex &parent) const return m_sourceInfo.count(); } +bool FlatObjectListModel::removeRows(int row, int count, const QModelIndex &parent) +{ + beginRemoveRows(parent, row, row + count - 1); + m_sourceInfo.remove(row, count); + endRemoveRows(); + return true; +} + void FlatObjectListModel::setSourceModel(ObjectListModel *sourceModel) { beginResetModel(); diff --git a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h index 89b5a93c..4013f15c 100644 --- a/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h +++ b/src/Authoring/Studio/Palettes/Inspector/ObjectListModel.h @@ -118,6 +118,7 @@ public: int role = Qt::DisplayRole) const; bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; void setSourceModel(ObjectListModel *sourceModel); ObjectListModel *sourceModel() const { return m_sourceModel; } diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp new file mode 100644 index 00000000..83e72e7b --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "VariantTagDialog.h" +#include "ui_VariantTagDialog.h" +#include "Dialogs.h" +#include "StudioApp.h" +#include "Core.h" +#include "ProjectFile.h" + +VariantTagDialog::VariantTagDialog(DialogType type, const QString &group, const QString &name, + QWidget *parent) + : QDialog(parent) + , m_type(type) + , m_group(group) + , m_ui(new Ui::VariantTagDialog) +{ + m_ui->setupUi(this); + + m_names.first = name; + + if (type == AddGroup) { + setWindowTitle(tr("Add new Group")); + m_ui->label->setText(tr("Group name")); + } else if (type == RenameGroup) { + setWindowTitle(tr("Rename Group")); + m_ui->label->setText(tr("Group name")); + m_ui->lineEditTagName->setText(name); + m_ui->lineEditTagName->selectAll(); + } else if (type == RenameTag) { + m_ui->lineEditTagName->setText(name); + m_ui->lineEditTagName->selectAll(); + } +} + +void VariantTagDialog::accept() +{ + QString name = m_ui->lineEditTagName->text(); + + if (name.isEmpty()) { + displayWarning(EmptyWarning); + } else if (name == m_names.first) { // no change + QDialog::reject(); + } else if (((m_type == AddGroup || m_type == RenameGroup) + && !g_StudioApp.GetCore()->getProjectFile().isVariantGroupUnique(name)) + || (!g_StudioApp.GetCore()->getProjectFile().isVariantTagUnique(m_group, name))) { + displayWarning(UniqueWarning); + } else { + m_names.second = name; + QDialog::accept(); + } +} + +std::pair<QString, QString> VariantTagDialog::getNames() const +{ + return m_names; +} + +void VariantTagDialog::displayWarning(WarningType warningType) +{ + QString warning; + if (warningType == EmptyWarning) { + if (m_type == AddGroup || m_type == RenameGroup) + warning = tr("The group name must not be empty."); + else + warning = tr("The tag name must not be empty."); + } else if (warningType == UniqueWarning) { + if (m_type == AddGroup || m_type == RenameGroup) + warning = tr("The group name must be unique."); + else + warning = tr("The tag name must be unique within the tag group."); + } + + g_StudioApp.GetDialogs()->DisplayMessageBox(tr("Warning"), warning, + Qt3DSMessageBox::ICON_WARNING, false); +} + +VariantTagDialog::~VariantTagDialog() +{ + delete m_ui; +} diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.h b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.h new file mode 100644 index 00000000..b5e3989f --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 VARIANTTAGDIALOG_H +#define VARIANTTAGDIALOG_H + +#include <QtWidgets/qdialog.h> + +#ifdef QT_NAMESPACE +using namespace QT_NAMESPACE; +#endif + +QT_BEGIN_NAMESPACE +namespace Ui { +class VariantTagDialog; +} +QT_END_NAMESPACE + +class VariantTagDialog : public QDialog +{ + Q_OBJECT + +public: + enum DialogType { AddTag, RenameTag, AddGroup, RenameGroup }; + + explicit VariantTagDialog(DialogType type, const QString &group = {}, const QString &name = {}, + QWidget *parent = nullptr); + ~VariantTagDialog() override; + +public Q_SLOTS: + void accept() override; + std::pair<QString, QString> getNames() const; + +private: + enum WarningType { + EmptyWarning, + UniqueWarning + }; + + void displayWarning(WarningType warningType); + + DialogType m_type; + QString m_group; + Ui::VariantTagDialog *m_ui; + std::pair<QString, QString> m_names; // holds the tags values before and after rename +}; + +#endif // VARIANTTAGDIALOG_H diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.ui b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.ui new file mode 100644 index 00000000..aa5d24ef --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantTagDialog.ui @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>VariantTagDialog</class> + <widget class="QDialog" name="VariantTagDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>241</width> + <height>89</height> + </rect> + </property> + <property name="windowTitle"> + <string>Add new Tag</string> + </property> + <property name="sizeGripEnabled"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QWidget" name="widget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QWidget" name="labelEditLayout" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout" stretch="0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Tag name</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEditTagName"/> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>VariantTagDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>VariantTagDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp new file mode 100644 index 00000000..a122889b --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "VariantsGroupModel.h" +#include "VariantsTagModel.h" +#include "StudioApp.h" +#include "Core.h" +#include "Qt3DSDMStudioSystem.h" +#include "ClientDataModelBridge.h" +#include "IDocumentEditor.h" +#include "VariantTagDialog.h" +#include "StudioUtils.h" +#include "Dialogs.h" + +#include <QtCore/qsavefile.h> + +VariantsGroupModel::VariantsGroupModel(QObject *parent) + : QAbstractListModel(parent) +{ + +} + +void VariantsGroupModel::refresh() +{ + int instance = g_StudioApp.GetCore()->GetDoc()->GetSelectedInstance(); + auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge(); + + if (instance == 0 || !bridge->IsLayerInstance(instance)) { + m_instance = 0; + m_property = 0; + return; + } + + auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetPropertySystem(); + m_instance = instance; + m_property = bridge->GetLayer().m_variants.m_Property; + + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(m_instance, m_property, sValue)) { + beginResetModel(); + m_data.clear(); + + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + QHash<QString, QStringList> propTags; + if (!propVal.isEmpty()) { + const QStringList propTagsList = propVal.split(QChar(',')); + for (auto &propTag : propTagsList) { + const QStringList propTagPair = propTag.split(QChar(':')); + propTags[propTagPair[0]].append(propTagPair[1]); + } + } + + // build the variants data model + const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef(); + const auto keys = variantsDef.keys(); + for (auto &group : keys) { + TagGroupData g; + g.m_title = group; + g.m_color = variantsDef[group].m_color; + + VariantsTagModel *m = new VariantsTagModel(this); + QVector<std::pair<QString, bool> > tags; + for (int i = 0; i < variantsDef[group].m_tags.length(); ++i) + tags.append({variantsDef[group].m_tags[i], + propTags[group].contains(variantsDef[group].m_tags[i])}); + + m->init(tags); + g.m_tagsModel = m; + + m_data.push_back(g); + } + + endResetModel(); + + bool isVariantsEmpty = rowCount() == 0; + if (m_variantsEmpty != isVariantsEmpty) { + m_variantsEmpty = isVariantsEmpty; + Q_EMIT varaintsEmptyChanged(); + } + } +} + +int VariantsGroupModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if (parent.isValid()) + return 0; + + return m_data.size(); +} + +QVariant VariantsGroupModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == GroupTitleRole) + return m_data.at(index.row()).m_title; + else if (role == GroupColorRole) + return m_data.at(index.row()).m_color; + else if (role == TagRole) + return QVariant::fromValue(m_data.at(index.row()).m_tagsModel); + + return QVariant(); +} + +void VariantsGroupModel::setTagState(const QString &group, const QString &tag, bool selected) +{ + QString val; + QString tagsStr; + bool skipFirst = false; + for (auto &g : qAsConst(m_data)) { + if (g.m_title == group) + g.m_tagsModel->updateTagState(tag, selected); + + tagsStr = g.m_tagsModel->serialize(g.m_title); + if (!tagsStr.isEmpty()) { + if (skipFirst) + val.append(QChar(',')); + val.append(tagsStr); + skipFirst = true; + } + } + + auto sVal = std::make_shared<qt3dsdm::CDataStr>(Q3DStudio::CString::fromQString(val)); + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property")) + ->SetInstancePropertyValue(m_instance, m_property, sVal); +} + +void VariantsGroupModel::addNewTag(const QString &group) +{ + VariantTagDialog dlg(VariantTagDialog::AddTag, group); + + if (dlg.exec() == QDialog::Accepted) { + g_StudioApp.GetCore()->getProjectFile().addVariantTag(group, dlg.getNames().second); + refresh(); + } +} + +void VariantsGroupModel::importVariants() +{ + QString importFilePath = g_StudioApp.GetDialogs()->getImportVariantsDlg(); + + if (!importFilePath.isEmpty()) { + g_StudioApp.GetCore()->getProjectFile().loadVariants(importFilePath); + refresh(); + } +} + +void VariantsGroupModel::exportVariants() +{ + QString exportFilePath = g_StudioApp.GetDialogs()->getExportVariantsDlg(); + + if (exportFilePath.isEmpty()) + return; + + QDomDocument domDoc; + domDoc.appendChild(domDoc.createProcessingInstruction(QStringLiteral("xml"), + QStringLiteral("version=\"1.0\"" + " encoding=\"utf-8\""))); + + const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef(); + const auto keys = variantsDef.keys(); + QDomElement vElem = domDoc.createElement(QStringLiteral("variants")); + domDoc.appendChild(vElem); + for (auto &g : keys) { + const auto group = variantsDef[g]; + QDomElement gElem = domDoc.createElement(QStringLiteral("variantgroup")); + gElem.setAttribute(QStringLiteral("id"), g); + gElem.setAttribute(QStringLiteral("color"), group.m_color); + vElem.appendChild(gElem); + + for (auto &t : qAsConst(group.m_tags)) { + QDomElement tElem = domDoc.createElement(QStringLiteral("variant"));; + tElem.setAttribute(QStringLiteral("id"), t); + gElem.appendChild(tElem); + } + } + + QSaveFile file(exportFilePath); + if (StudioUtils::openTextSave(file)) + StudioUtils::commitDomDocumentSave(file, domDoc); +} + +void VariantsGroupModel::addNewGroup() +{ + VariantTagDialog dlg(VariantTagDialog::AddGroup); + + if (dlg.exec() == QDialog::Accepted) { + g_StudioApp.GetCore()->getProjectFile().addVariantGroup(dlg.getNames().second); + refresh(); + } +} + +QHash<int, QByteArray> VariantsGroupModel::roleNames() const +{ + auto names = QAbstractListModel::roleNames(); + names.insert(GroupTitleRole, "group"); + names.insert(GroupColorRole, "color"); + names.insert(TagRole, "tags"); + return names; +} + +Qt::ItemFlags VariantsGroupModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return Qt::ItemIsEditable; +} diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h new file mode 100644 index 00000000..75a218f7 --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantsGroupModel.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 VARIANTSGROUPMODEL_H +#define VARIANTSGROUPMODEL_H + +#include <QtCore/qabstractitemmodel.h> + +class VariantsTagModel; + +class VariantsGroupModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(bool variantsEmpty MEMBER m_variantsEmpty NOTIFY varaintsEmptyChanged) + +public: +Q_SIGNALS: + void varaintsEmptyChanged(); + +public: + explicit VariantsGroupModel(QObject *parent = nullptr); + + enum Roles { + GroupTitleRole = Qt::UserRole + 1, + GroupColorRole, + TagRole + }; + + struct TagGroupData + { + QString m_title; + QString m_color; + VariantsTagModel *m_tagsModel = nullptr; + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = GroupTitleRole) const override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + void refresh(); + + Q_INVOKABLE void setTagState(const QString &group, const QString &tag, bool selected); + Q_INVOKABLE void addNewTag(const QString &group); + Q_INVOKABLE void addNewGroup(); + Q_INVOKABLE void importVariants(); + Q_INVOKABLE void exportVariants(); + + +protected: + QHash<int, QByteArray> roleNames() const override; + +private: + QVector<TagGroupData> m_data; + int m_instance = 0; // selected layer instance + int m_property = 0; // variant tags property handler + bool m_variantsEmpty = true; // no groups (nor tags) +}; + +#endif // VARIANTSGROUPMODEL_H diff --git a/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp new file mode 100644 index 00000000..910b4f09 --- /dev/null +++ b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "VariantsTagModel.h" + +VariantsTagModel::VariantsTagModel(QObject *parent) + : QAbstractListModel(parent) +{ + +} + +void VariantsTagModel::init(const QVector<std::pair<QString, bool> > &data) +{ + m_data = data; +} + +void VariantsTagModel::updateTagState(const QString &tag, bool selected) +{ + for (auto &t : m_data) { + if (t.first == tag) { + t.second = selected; + break; + } + } +} + +// return the tags in a formatted string to be saved to the property +QString VariantsTagModel::serialize(const QString &groupName) const +{ + QString ret; + bool skipFirst = false; + for (auto &t : qAsConst(m_data)) { + if (t.second) { + if (skipFirst) + ret.append(QLatin1Char(',')); + + ret.append(groupName + QLatin1Char(':') + t.first); + + skipFirst = true; + } + } + + return ret; +} + +int VariantsTagModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if (parent.isValid()) + return 0; + + return m_data.size(); +} + +QVariant VariantsTagModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == TagRole) + return m_data.at(index.row()).first; + else if (role == SelectedRole) + return m_data.at(index.row()).second; + + return QVariant(); +} + +QHash<int, QByteArray> VariantsTagModel::roleNames() const +{ + auto names = QAbstractListModel::roleNames(); + names.insert(TagRole, "tag"); + names.insert(SelectedRole, "selected"); + return names; +} + +Qt::ItemFlags VariantsTagModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + return Qt::ItemIsEditable; // FIXME: Implement me! +} diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.h index e1a6914f..01bb4e73 100644 --- a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineKeyframesManager.h +++ b/src/Authoring/Studio/Palettes/Inspector/VariantsTagModel.h @@ -1,7 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2008 NVIDIA Corporation. -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt 3D Studio. @@ -27,29 +26,37 @@ ** ****************************************************************************/ -#ifndef INCLUDED_ITIMELINE_KEYFRAMES_MANAGER_H -#define INCLUDED_ITIMELINE_KEYFRAMES_MANAGER_H 1 +#ifndef VARIANTSTAGMODEL_H +#define VARIANTSTAGMODEL_H -#pragma once +#include <QtCore/qabstractitemmodel.h> -#include "IKeyframesManager.h" - -//============================================================================= -/** - * Interface to manage keyframes related actions in the Timeline - */ -//============================================================================= -class ITimelineKeyframesManager : public IKeyframesManager +class VariantsTagModel : public QAbstractListModel { + Q_OBJECT + public: - virtual ~ITimelineKeyframesManager() {} - - virtual void SetKeyframeTime(long inTime) = 0; - virtual void SetKeyframesDynamic(bool inDynamic) = 0; - virtual long OffsetSelectedKeyframes(long inOffset) = 0; - virtual bool CanMakeSelectedKeyframesDynamic() = 0; - virtual void CommitChangedKeyframes() = 0; - virtual void RollbackChangedKeyframes() = 0; + explicit VariantsTagModel(QObject *parent = nullptr); + + enum Roles { + TagRole = Qt::UserRole + 1, + SelectedRole, + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = TagRole) const override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + void init(const QVector<std::pair<QString, bool> > &data); + void updateTagState(const QString &tag, bool selected); + QString serialize(const QString &groupName) const; + +protected: + QHash<int, QByteArray> roleNames() const override; + +private: + QVector<std::pair<QString, bool> > m_data; // [{tagName, selectedState}, ...] }; -#endif // INCLUDED_IKEYFRAMES_MANAGER_H +#endif // VARIANTSTAGMODEL_H diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp index 0a098a5b..f6d498f2 100644 --- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp +++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp @@ -454,6 +454,24 @@ void ProjectFileSystemModel::setRootPath(const QString &path) m_projectReferencesUpdateMap.clear(); m_projectReferencesUpdateTimer.stop(); + // Delete the old model. If the new project is in a totally different directory tree, not + // doing this will result in unexplicable crashes when trying to parse something that should + // not be parsed. + disconnect(m_model, &QAbstractItemModel::rowsInserted, + this, &ProjectFileSystemModel::modelRowsInserted); + disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ProjectFileSystemModel::modelRowsRemoved); + disconnect(m_model, &QAbstractItemModel::layoutChanged, + this, &ProjectFileSystemModel::modelLayoutChanged); + delete m_model; + m_model = new QFileSystemModel(this); + connect(m_model, &QAbstractItemModel::rowsInserted, + this, &ProjectFileSystemModel::modelRowsInserted); + connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ProjectFileSystemModel::modelRowsRemoved); + connect(m_model, &QAbstractItemModel::layoutChanged, + this, &ProjectFileSystemModel::modelLayoutChanged); + setRootIndex(m_model->setRootPath(path)); // Open the presentations folder by default diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp index 710dd4b8..9229e519 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp +++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.cpp @@ -56,6 +56,10 @@ QVariant SlideModel::data(const QModelIndex &index, int role) const return slideName(m_slides[row]); case SelectedRole: return row == m_selectedRow; + case VariantsRole: + int slideIdx = GetDoc()->GetStudioSystem()->GetSlideSystem()->GetSlideIndex(m_slides[row]); + if (slideIdx < m_variants.size()) + return m_variants.at(slideIdx); } return {}; @@ -91,7 +95,7 @@ bool SlideModel::setData(const QModelIndex &index, const QVariant &value, int ro } Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {role}); - return true; + break; } default: return false; @@ -112,6 +116,7 @@ QHash<int, QByteArray> SlideModel::roleNames() const { auto names = QAbstractListModel::roleNames(); names.insert(NameRole, "name"); + names.insert(VariantsRole, "variants"); names.insert(SelectedRole, "selected"); return names; @@ -347,6 +352,61 @@ void SlideModel::setSlideName(const qt3dsdm::Qt3DSDMSlideHandle &handle, const Q } } +void SlideModel::refreshVariants(const QStringList &variants) +{ + m_variants.clear(); + + if (variants.isEmpty()) { + const auto *slideSystem = GetDoc()->GetStudioSystem()->GetSlideSystem(); + int slideCount = slideSystem->GetSlideCount(slideSystem->GetMasterSlide( + GetDoc()->GetActiveSlide())); + + QString vTemplate = QStringLiteral(" <font color='%1'>%2</font>"); + QVector<QHash<QString, int>> counts(slideCount); // <group, total count tags> + + const auto propertySystem = GetDoc()->GetPropertySystem(); + const auto layers = GetDoc()->getLayers(); + for (auto layer : layers) { + int slideIdx = slideIndex(slideSystem->GetAssociatedSlide(layer)); + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, GetBridge()->GetLayer().m_variants, + sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + if (!propVal.isEmpty()) { + QStringList tagPairs = propVal.split(QLatin1Char(',')); + for (int i = 0; i < tagPairs.size(); ++i) { + QString group = tagPairs[i].left(tagPairs[i].indexOf(QLatin1Char(':'))); + ++counts[slideIdx][group]; + } + } + } + } + + // add master slide layers counts to other layers + const auto keys = counts[0].keys(); + for (int i = 1; i < slideCount; ++i) { + for (auto g : keys) + counts[i][g] += counts[0][g]; + } + + // update the variants counts model (m_variants) + auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef(); + for (int i = 0; i < counts.size(); ++i) { // slides indexes + QString slideVariants; + const auto keys = counts[i].keys(); + for (auto g : keys) // variants groups + slideVariants.append(vTemplate.arg(variantsDef[g].m_color).arg(counts[i][g])); + + m_variants << slideVariants; + } + } else { + m_variants = variants; + } + + Q_EMIT dataChanged(this->index(0, 0), this->index(rowCount() - 1, 0), {VariantsRole}); +} + CDoc *SlideModel::GetDoc() const { return g_StudioApp.GetCore()->GetDoc(); @@ -375,10 +435,10 @@ void SlideModel::refreshSlideLabel(qt3dsdm::Qt3DSDMInstanceHandle instanceHandle if (m_slides[i] == slideHandle) { setData(index(i, 0), GetBridge()->GetName(instanceHandle), SlideModel::NameRole); + break; } } } - } // Set selected slide highlight on UI diff --git a/src/Authoring/Studio/Palettes/Slide/SlideModel.h b/src/Authoring/Studio/Palettes/Slide/SlideModel.h index 1de717cd..6f2de22f 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideModel.h +++ b/src/Authoring/Studio/Palettes/Slide/SlideModel.h @@ -44,7 +44,8 @@ public: enum Roles { NameRole = Qt::DisplayRole, HandleRole = Qt::UserRole + 1, - SelectedRole + SelectedRole, + VariantsRole }; SlideModel(int slideCount, QObject *parent = nullptr); @@ -76,6 +77,8 @@ public: void refreshSlideLabel(qt3dsdm::Qt3DSDMInstanceHandle instanceHandle, qt3dsdm::Qt3DSDMPropertyHandle propertyHandle); void setSelectedSlideIndex(const QModelIndex &index); + void refreshVariants(const QStringList &variants = {}); + QStringList variants() const { return m_variants; } private: bool hasSlideWithName(const QString &name) const; @@ -89,6 +92,7 @@ private: int m_selectedRow = -1; int m_rearrangeStartRow = -1; int m_rearrangeEndRow = -1; + QStringList m_variants; // model for variants tags display QHash<qt3dsdm::Qt3DSDMInstanceHandle, qt3dsdm::Qt3DSDMSlideHandle> m_slideLookupHash; }; diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.cpp b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp index 5bb5aa67..51f90372 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.cpp +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.cpp @@ -27,11 +27,9 @@ ****************************************************************************/ #include "SlideView.h" -#include <QtGui/qcolor.h> #include "Core.h" #include "Dispatch.h" #include "Doc.h" -#include "Literals.h" #include "StudioPreferences.h" #include "SlideModel.h" #include "StudioApp.h" @@ -45,22 +43,19 @@ #include "Qt3DSDMSlides.h" #include "Dialogs.h" -#include <QtCore/qcoreapplication.h> #include <QtCore/qtimer.h> #include <QtQml/qqmlcontext.h> #include <QtQml/qqmlengine.h> -#include <QtWidgets/qdesktopwidget.h> -#include <QtWidgets/qdockwidget.h> SlideView::SlideView(QWidget *parent) : QQuickWidget(parent) , m_MasterSlideModel(new SlideModel(1, this)) , m_SlidesModel(new SlideModel(0, this)) + , m_CurrentModel(m_SlidesModel) , m_ActiveRoot(0) , m_toolTip(tr("No Controller")) { g_StudioApp.GetCore()->GetDispatch()->AddPresentationChangeListener(this); setResizeMode(QQuickWidget::SizeRootObjectToView); - m_CurrentModel = m_SlidesModel; QTimer::singleShot(0, this, &SlideView::initialize); } @@ -82,10 +77,7 @@ void SlideView::setShowMasterSlide(bool show) if (show == currentIsMaster) return; - if (show) - m_CurrentModel = m_MasterSlideModel; - else - m_CurrentModel = m_SlidesModel; + m_CurrentModel = show ? m_MasterSlideModel : m_SlidesModel; // We need to get the first slide in the correct master mode CDoc *theDoc = GetDoc(); @@ -124,8 +116,6 @@ void SlideView::showControllerDialog(const QPoint &point) m_dataInputSelector->setData(dataInputList, currCtr); CDialogs::showWidgetBrowser(this, m_dataInputSelector, point, CDialogs::WidgetBrowserAlign::ToolButton); - - return; } bool SlideView::toolTipsEnabled() @@ -142,7 +132,7 @@ QSize SlideView::minimumSizeHint() const { // prevent datainput control indicator from overlapping // with slide name too much when panel is minimised - return {80, 0}; + return {100, 0}; } void SlideView::deselectAll() @@ -209,8 +199,9 @@ void SlideView::OnNewPresentation() // Set up listener for the name changes to slide m_Connections.push_back(theSignalProvider->ConnectInstancePropertyValue( - std::bind(&SlideModel::refreshSlideLabel, m_SlidesModel, + std::bind(&SlideView::onPropertyChanged, this, std::placeholders::_1, std::placeholders::_2))); + // Set up listener for undo/redo changes in order to update // slide datainput control CDispatch *theDispatch = g_StudioApp.GetCore()->GetDispatch(); @@ -290,18 +281,16 @@ void SlideView::onDataInputChange(int handle, int instance, const QString &dataI m_toolTip = tr("No Controller"); } qt3dsdm::Qt3DSDMPropertyHandle ctrldProp; - if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_SCENE) { + if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_SCENE) ctrldProp = bridge->GetObjectDefinitions().m_Scene.m_ControlledProperty; - } else if (bridge->GetObjectType(slideRoot) == - EStudioObjectType::OBJTYPE_COMPONENT) { + else if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_COMPONENT) ctrldProp = bridge->GetObjectDefinitions().m_Component.m_ControlledProperty; - } else { + else Q_ASSERT(false); - } qt3dsdm::SValue controlledPropertyVal; - doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue( - slideRoot, ctrldProp, controlledPropertyVal); + doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue(slideRoot, ctrldProp, + controlledPropertyVal); // To indicate that slide transitions are controlled by data input, // we set "controlled property" of this scene to contain the name of @@ -309,27 +298,27 @@ void SlideView::onDataInputChange(int handle, int instance, const QString &dataI // If we have existing slide control in this root element, replace it. // Otherwise just append slide control string to controlledproperty // (it might already contain timeline control information) - auto existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal); - if (existingCtrl.contains("@slide")) { - int slideStrPos = existingCtrl.indexOf("@slide"); + QString existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal); + if (existingCtrl.contains(QLatin1String("@slide"))) { + int slideStrPos = existingCtrl.indexOf(QLatin1String("@slide")); // find the controlling datainput name and build the string to replace - int ctrStrPos = existingCtrl.lastIndexOf("$", slideStrPos - 2); + int ctrStrPos = existingCtrl.lastIndexOf(QLatin1Char('$'), slideStrPos - 2); QString prevCtrler = existingCtrl.mid(ctrStrPos, slideStrPos - ctrStrPos - 1); - existingCtrl.replace(prevCtrler + " @slide", fullSlideControlStr); + existingCtrl.replace(prevCtrler + QLatin1String(" @slide"), fullSlideControlStr); } else { - (!existingCtrl.isEmpty() && m_controlled) ? existingCtrl.append(" ") : 0; + if (!existingCtrl.isEmpty() && m_controlled) + existingCtrl.append(QLatin1Char(' ')); existingCtrl.append(fullSlideControlStr); } - if (existingCtrl.endsWith(" ")) + if (existingCtrl.endsWith(QLatin1Char(' '))) existingCtrl.chop(1); - if (existingCtrl.startsWith(" ")) + if (existingCtrl.startsWith(QLatin1Char(' '))) existingCtrl.remove(0, 1); qt3dsdm::SValue fullCtrlPropVal - = std::make_shared<qt3dsdm::CDataStr>( - Q3DStudio::CString::fromQString(existingCtrl)); + = std::make_shared<qt3dsdm::CDataStr>(Q3DStudio::CString::fromQString(existingCtrl)); Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Slide control")) ->SetInstancePropertyValue(slideRoot, ctrldProp, fullCtrlPropVal); @@ -338,6 +327,17 @@ void SlideView::onDataInputChange(int handle, int instance, const QString &dataI Q_EMIT controlledChanged(); } +void SlideView::onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty) +{ + // refresh slide name + m_SlidesModel->refreshSlideLabel(inInstance, inProperty); + + // refresh variants + if (inProperty == GetBridge()->GetLayer().m_variants) + refreshVariants(); +} + void SlideView::onDockLocationChange(Qt::DockWidgetArea area) { m_dockArea = area; @@ -353,24 +353,22 @@ void SlideView::updateDataInputStatus() qt3dsdm::Qt3DSDMInstanceHandle slideRoot = doc->GetActiveRootInstance(); qt3dsdm::Qt3DSDMPropertyHandle ctrldProp; - if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_SCENE) { + if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_SCENE) ctrldProp = bridge->GetObjectDefinitions().m_Scene.m_ControlledProperty; - } else if (bridge->GetObjectType(slideRoot) == - EStudioObjectType::OBJTYPE_COMPONENT) { + else if (bridge->GetObjectType(slideRoot) == EStudioObjectType::OBJTYPE_COMPONENT) ctrldProp = bridge->GetObjectDefinitions().m_Component.m_ControlledProperty; - } else { + else Q_ASSERT(false); - } qt3dsdm::SValue controlledPropertyVal; - doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue( - slideRoot, ctrldProp, controlledPropertyVal); - auto existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal); + doc->GetStudioSystem()->GetPropertySystem()->GetInstancePropertyValue(slideRoot, ctrldProp, + controlledPropertyVal); + QString existingCtrl = qt3dsdm::get<QString>(controlledPropertyVal); QString newController; - int slideStrPos = existingCtrl.indexOf("@slide"); + int slideStrPos = existingCtrl.indexOf(QLatin1String("@slide")); if (slideStrPos != -1) { - int ctrStrPos = existingCtrl.lastIndexOf("$", slideStrPos - 2); + int ctrStrPos = existingCtrl.lastIndexOf(QLatin1Char('$'), slideStrPos - 2); newController = existingCtrl.mid(ctrStrPos + 1, slideStrPos - ctrStrPos - 2); } if (newController != m_currentController) { @@ -505,6 +503,12 @@ bool SlideView::isMaster(const qt3dsdm::Qt3DSDMSlideHandle &inSlideHandle) return (0 == GetSlideIndex(inSlideHandle)); } +void SlideView::refreshVariants() +{ + m_SlidesModel->refreshVariants(); + m_MasterSlideModel->refreshVariants(m_SlidesModel->variants()); +} + void SlideView::OnBeginDataModelNotifications() { } diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.h b/src/Authoring/Studio/Palettes/Slide/SlideView.h index e95ac314..18c4d1d7 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.h +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.h @@ -67,6 +67,7 @@ public: QSize minimumSizeHint() const override; void onDataInputChange(int handle, int instance, const QString &dataInputName); void onDockLocationChange(Qt::DockWidgetArea area); + void refreshVariants(); Q_INVOKABLE void deselectAll(); Q_INVOKABLE void addNewSlide(int row); @@ -120,10 +121,12 @@ private: long GetSlideIndex(const qt3dsdm::Qt3DSDMSlideHandle &inSlideHandle); bool isMaster(const qt3dsdm::Qt3DSDMSlideHandle &inSlideHandle); void rebuildSlideList(const qt3dsdm::Qt3DSDMSlideHandle &inActiveSlideHandle); + void onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance, + qt3dsdm::Qt3DSDMPropertyHandle inProperty); - SlideModel *m_CurrentModel = nullptr; SlideModel *m_MasterSlideModel = nullptr; SlideModel *m_SlidesModel = nullptr; + SlideModel *m_CurrentModel = nullptr; DataInputSelectView *m_dataInputSelector = nullptr; QColor m_BaseColor = QColor::fromRgb(75, 75, 75); std::vector<std::shared_ptr<qt3dsdm::ISignalConnection>> diff --git a/src/Authoring/Studio/Palettes/Slide/SlideView.qml b/src/Authoring/Studio/Palettes/Slide/SlideView.qml index c8f409d0..4113c947 100644 --- a/src/Authoring/Studio/Palettes/Slide/SlideView.qml +++ b/src/Authoring/Studio/Palettes/Slide/SlideView.qml @@ -263,6 +263,17 @@ Rectangle { } } + Label { // variants + width: slideImage.width + font.pixelSize: _fontSize + padding: 3 + verticalAlignment: Text.AlignVCenter + background: Rectangle { color:"#111111" } + wrapMode: Text.WordWrap + visible: model.variants !== "" + text: model.variants + } + Item { anchors.horizontalCenter: slideImage.horizontalCenter diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h index e8cdd767..a01e5e48 100644 --- a/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h +++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/ITimelineItemBinding.h @@ -37,7 +37,6 @@ class RowTree; class CControlWindowListener; -class ITimelineKeyframesManager; // Data model specific ?? class CDropTarget; diff --git a/src/Authoring/Studio/Palettes/Timeline/Bindings/Qt3DSDMTimelineTimebar.cpp b/src/Authoring/Studio/Palettes/Timeline/Bindings/Qt3DSDMTimelineTimebar.cpp index ba6dd622..fdeb7338 100644 --- a/src/Authoring/Studio/Palettes/Timeline/Bindings/Qt3DSDMTimelineTimebar.cpp +++ b/src/Authoring/Studio/Palettes/Timeline/Bindings/Qt3DSDMTimelineTimebar.cpp @@ -37,7 +37,6 @@ #include "Doc.h" #include "Dispatch.h" #include "Core.h" -#include "DurationEditDlg.h" #include "IDocumentEditor.h" #include "StudioFullSystem.h" #include "StudioPreferences.h" @@ -218,6 +217,5 @@ void Qt3DSDMTimelineTimebar::SetTimebarComment(const Q3DStudio::CString &inComme void Qt3DSDMTimelineTimebar::SetTimebarTime(ITimeChangeCallback *inCallback /*= nullptr*/) { g_StudioApp.GetDialogs()->asyncDisplayDurationEditDialog(GetStartTime(), GetEndTime(), - m_TimelineTranslationManager->GetDoc(), inCallback); } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/Keyframe.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/Keyframe.h index c803ef2c..7e4ea614 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/Keyframe.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/Keyframe.h @@ -35,7 +35,7 @@ struct Keyframe { - Keyframe(double time, RowTimeline *propRow) + Keyframe(long time, RowTimeline *propRow) : time(time) , rowProperty(propRow) , rowMaster(propRow->parentRow()) @@ -47,7 +47,7 @@ struct Keyframe return binding && binding->IsSelected(); } - double time; + long time; QString propertyType; RowTimeline *rowProperty = nullptr; RowTimeline *rowMaster = nullptr; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.cpp index 936d60ae..9a12aab3 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.cpp @@ -30,13 +30,8 @@ #include "RowTree.h" #include "RowTimeline.h" #include "Keyframe.h" -#include "RowTypes.h" -#include "TimelineConstants.h" -#include "Ruler.h" -#include "PlayHead.h" #include "RowManager.h" #include "TimelineGraphicsScene.h" -#include "StudioObjectTypes.h" #include "StudioApp.h" #include "Core.h" #include "Doc.h" @@ -44,26 +39,16 @@ #include "CmdDataModelRemoveKeyframe.h" #include "CmdDataModelInsertKeyframe.h" #include "CmdDataModelChangeKeyframe.h" -#include "Qt3DSDMAnimation.h" #include "ClientDataModelBridge.h" -#include "Bindings/ITimelineItemBinding.h" #include "Bindings/OffsetKeyframesCommandHelper.h" -#include "Bindings/Qt3DSDMTimelineKeyframe.h" +#include "Bindings/PasteKeyframesCommandHelper.h" #include "StudioPreferences.h" -#include "Qt3DSDMAnimation.h" #include "Dialogs.h" -#include "TimeEditDlg.h" -#include "Bindings/PasteKeyframesCommandHelper.h" - -#include <qglobal.h> -#include <QtCore/qhash.h> -#include <QtCore/qdebug.h> +#include "TimeEnums.h" using namespace qt3dsdm; -KeyframeManager::KeyframeManager(TimelineGraphicsScene *scene) - : m_scene(scene) - , m_pasteKeyframeCommandHelper(nullptr) +KeyframeManager::KeyframeManager(TimelineGraphicsScene *scene) : m_scene(scene) { } @@ -72,7 +57,7 @@ KeyframeManager::~KeyframeManager() delete m_pasteKeyframeCommandHelper; } -QList<Keyframe *> KeyframeManager::insertKeyframe(RowTimeline *row, double time, +QList<Keyframe *> KeyframeManager::insertKeyframe(RowTimeline *row, long time, bool selectInsertedKeyframes) { QList<Keyframe *> addedKeyframes; @@ -88,9 +73,8 @@ QList<Keyframe *> KeyframeManager::insertKeyframe(RowTimeline *row, double time, } if (!propRows.empty()) { - Keyframe *keyframe = nullptr; for (const auto &r : qAsConst(propRows)) { - keyframe = new Keyframe(time, r); + Keyframe *keyframe = new Keyframe(time, r); r->insertKeyframe(keyframe); r->parentRow()->insertKeyframe(keyframe); addedKeyframes.append(keyframe); @@ -159,10 +143,9 @@ void KeyframeManager::commitMoveSelectedKeyframes() { CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); COffsetKeyframesCommandHelper h(*theDoc); - for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) { - const long msTime = round(keyframe->time * 1000); - keyframe->binding->UpdateKeyframesTime(&h, msTime); - } + + for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) + keyframe->binding->UpdateKeyframesTime(&h, keyframe->time); } void KeyframeManager::selectKeyframesInRect(const QRectF &rect) @@ -300,7 +283,7 @@ void KeyframeManager::copySelectedKeyframes() m_pasteKeyframeCommandHelper = new CPasteKeyframeCommandHelper(); // calc min copied frames time - double minTime = 999999.0; // in seconds (~277.78 hrs) + long minTime = LONG_MAX; for (auto keyframe : qAsConst(m_selectedKeyframes)) { if (keyframe->time < minTime) minTime = keyframe->time; @@ -331,9 +314,9 @@ void KeyframeManager::copySelectedKeyframes() break; } - double dt = Qt3DSDMTimelineKeyframe::GetTimeInSecs(kf->GetTime()) - minTime; - qt3dsdm::Qt3DSDMAnimationHandle animation = - animationCore->GetAnimationForKeyframe(theKeyframeHandles[0]); + float dt = Qt3DSDMTimelineKeyframe::GetTimeInSecs(kf->GetTime() - minTime); + qt3dsdm::Qt3DSDMAnimationHandle animation + = animationCore->GetAnimationForKeyframe(theKeyframeHandles[0]); m_pasteKeyframeCommandHelper->AddKeyframeData( animationCore->GetAnimationInfo(animation).m_Property, dt, info, infoCount); } @@ -373,28 +356,47 @@ void KeyframeManager::pasteKeyframes() } } -void KeyframeManager::moveSelectedKeyframes(double dx) +void KeyframeManager::moveSelectedKeyframes(long newTime) { - double dt = m_scene->ruler()->distanceToTime(dx); + Keyframe *pressedKeyframe = m_scene->pressedKeyframe(); - if (dt < 0) { // check min limit - double minTime = 999999; // seconds (~277.78 hrs) - for (auto keyframe : qAsConst(m_selectedKeyframes)) { - if (keyframe->time < minTime) - minTime = keyframe->time; - } + Q_ASSERT(pressedKeyframe); - if (minTime + dt < 0) - dt = -minTime; - } + // make sure the min-time keyframe doesn't go below zero + long minTime = getMinSelectedKeyframesTime(); + if (pressedKeyframe->time - minTime > newTime) + newTime = pressedKeyframe->time - minTime; - for (auto keyframe : qAsConst(m_selectedKeyframes)) - keyframe->time += dt; + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + if (keyframe != pressedKeyframe) + keyframe->time = newTime - (pressedKeyframe->time - keyframe->time); + } + pressedKeyframe->time = newTime; for (auto row : qAsConst(m_selectedKeyframesMasterRows)) row->updateKeyframes(); } +long KeyframeManager::getMinSelectedKeyframesTime() const +{ + long minTime = LONG_MAX; + for (auto keyframe : qAsConst(m_selectedKeyframes)) { + if (keyframe->time < minTime) + minTime = keyframe->time; + } + + return minTime; +} + +// returns the distance between the pressed keyframe and the min-time keyframe in a multiselection +long KeyframeManager::getPressedKeyframeOffset() const +{ + if (m_scene->pressedKeyframe()) + return m_scene->pressedKeyframe()->time - getMinSelectedKeyframesTime(); + + return 0; +} + // selected keyframes belong to only one master row bool KeyframeManager::oneMasterRowSelected() const { @@ -422,9 +424,7 @@ bool KeyframeManager::hasDynamicKeyframes(RowTree *row) const return false; } -// IKeyframesManager interface to connect Doc and KeyframeManager -// Mahmoud_TODO: rewrite a better interface for the new timeline -// ITimelineKeyframesManager interface +// IKeyframesManager interface void KeyframeManager::SetKeyframeTime(long inTime) { g_StudioApp.GetDialogs()->asyncDisplayTimeEditDialog(inTime, g_StudioApp.GetCore()->GetDoc(), @@ -459,35 +459,25 @@ void KeyframeManager::SetKeyframesDynamic(bool inDynamic) g_StudioApp.GetCore()->ExecuteCommand(cmd); } -long KeyframeManager::OffsetSelectedKeyframes(long inOffset) -{ - double dx = m_scene->ruler()->timeToDistance(inOffset / 1000.0); - moveSelectedKeyframes(dx); - return 0; -} - -bool KeyframeManager::CanMakeSelectedKeyframesDynamic() -{ - // Mahmoud_TODO: implement if needed - return false; -} - void KeyframeManager::CommitChangedKeyframes() { + m_scene->resetPressedKeyframe(); commitMoveSelectedKeyframes(); } void KeyframeManager::RollbackChangedKeyframes() { + m_scene->resetPressedKeyframe(); + for (Keyframe *keyframe : qAsConst(m_selectedKeyframes)) - keyframe->time = keyframe->binding->GetTime() / 1000.0; + keyframe->time = keyframe->binding->GetTime(); for (auto row : qAsConst(m_selectedKeyframesMasterRows)) row->updateKeyframes(); } // IKeyframesManager interface -bool KeyframeManager::HasSelectedKeyframes(bool inOnlyDynamic) +bool KeyframeManager::HasSelectedKeyframes() { return hasSelectedKeyframes(); } @@ -571,11 +561,6 @@ void KeyframeManager::SetKeyframeInterpolation() } } -void KeyframeManager::SelectAllKeyframes() -{ - // Mahmoud_TODO: implement if needed -} - void KeyframeManager::DeselectAllKeyframes() { deselectAllKeyframes(); diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.h index 6af9896f..9c160687 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/KeyframeManager.h @@ -29,10 +29,10 @@ #ifndef KEYFRAMEMANAGER_H #define KEYFRAMEMANAGER_H -#include "Bindings/ITimelineKeyframesManager.h" +#include "IKeyframesManager.h" +#include "Qt3DSDMAnimation.h" #include <QtCore/qlist.h> #include <StudioObjectTypes.h> -#include "Qt3DSDMAnimation.h" class RowTimeline; class RowTree; @@ -43,13 +43,13 @@ struct Keyframe; QT_FORWARD_DECLARE_CLASS(QGraphicsSceneContextMenuEvent) QT_FORWARD_DECLARE_CLASS(QRectF) -class KeyframeManager : public ITimelineKeyframesManager +class KeyframeManager : public IKeyframesManager { public: KeyframeManager(TimelineGraphicsScene *m_scene); - virtual ~KeyframeManager(); + virtual ~KeyframeManager() override; - QList<Keyframe *> insertKeyframe(RowTimeline *row, double time, + QList<Keyframe *> insertKeyframe(RowTimeline *row, long time, bool selectInsertedKeyframes = true); void selectKeyframe(Keyframe *keyframe); void selectConnectedKeyframes(Keyframe *keyframe); @@ -63,7 +63,7 @@ public: void deleteKeyframes(RowTimeline *row, bool repaint = true); void copySelectedKeyframes(); void pasteKeyframes(); - void moveSelectedKeyframes(double dx); + void moveSelectedKeyframes(long newTime); void commitMoveSelectedKeyframes(); bool deleteSelectedKeyframes(); bool oneMasterRowSelected() const; @@ -72,16 +72,11 @@ public: bool hasDynamicKeyframes(RowTree *row) const; // IKeyframesManager interface - // Mahmoud_TODO: rewrite a better interface for the new timeline - // ITimelineKeyframesManager interface void SetKeyframeTime(long inTime) override; void SetKeyframesDynamic(bool inDynamic) override; - long OffsetSelectedKeyframes(long inOffset) override; - bool CanMakeSelectedKeyframesDynamic() override; void CommitChangedKeyframes() override; void RollbackChangedKeyframes() override; - // IKeyframesManager interface - bool HasSelectedKeyframes(bool inOnlyDynamic) override; + bool HasSelectedKeyframes() override; bool HasDynamicKeyframes() override; bool CanPerformKeyframeCopy() override; bool CanPerformKeyframePaste() override; @@ -89,14 +84,16 @@ public: bool RemoveKeyframes(bool inPerformCopy) override; void PasteKeyframes() override; void SetKeyframeInterpolation() override; - void SelectAllKeyframes() override; void DeselectAllKeyframes() override; void SetChangedKeyframes() override; + long getPressedKeyframeOffset() const; private: qt3dsdm::SGetOrSetKeyframeInfo setKeyframeInfo(qt3dsdm::Qt3DSDMKeyframeHandle inKeyframe, qt3dsdm::IAnimationCore &inCore); - CPasteKeyframeCommandHelper *m_pasteKeyframeCommandHelper; + long getMinSelectedKeyframesTime() const; + + CPasteKeyframeCommandHelper *m_pasteKeyframeCommandHelper = nullptr; TimelineGraphicsScene *m_scene; QList<Keyframe *> m_selectedKeyframes; QList<RowTimeline *> m_selectedKeyframesMasterRows; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/RowManager.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/RowManager.cpp index dae3d80a..6854b49b 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/RowManager.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/RowManager.cpp @@ -98,8 +98,8 @@ RowTree *RowManager::createRowFromBinding(ITimelineItemBinding *binding, RowTree ITimelineTimebar *timebar = binding->GetTimelineItem()->GetTimebar(); RowTimeline *rowTimeline = newRow->rowTimeline(); rowTimeline->clearBoundChildren(); - rowTimeline->setStartTime(timebar->GetStartTime() * .001); - rowTimeline->setEndTime(timebar->GetEndTime() * .001); + rowTimeline->setStartTime(timebar->GetStartTime()); + rowTimeline->setEndTime(timebar->GetEndTime()); rowTimeline->setBarColor(timebar->GetTimebarColor()); // create property rows @@ -113,12 +113,12 @@ RowTree *RowManager::createRowFromBinding(ITimelineItemBinding *binding, RowTree // add keyframes for (int j = 0; j < prop_i->GetKeyframeCount(); j++) { - Qt3DSDMTimelineKeyframe *kf = - static_cast<Qt3DSDMTimelineKeyframe *>(prop_i->GetKeyframeByIndex(j)); + Qt3DSDMTimelineKeyframe *kf + = static_cast<Qt3DSDMTimelineKeyframe *>(prop_i->GetKeyframeByIndex(j)); - QList<Keyframe *> addedKeyframes = - m_scene->keyframeManager()->insertKeyframe(propRow->rowTimeline(), - static_cast<double>(kf->GetTime()) * .001, false); + QList<Keyframe *> addedKeyframes + = m_scene->keyframeManager()->insertKeyframe(propRow->rowTimeline(), + kf->GetTime(), false); Keyframe *kfUI = addedKeyframes.at(0); kf->setUI(kfUI); @@ -151,7 +151,7 @@ RowTree *RowManager::getOrCreatePropertyRow(RowTree *masterRow, const QString &p { RowTree *propertyRow = masterRow->getPropertyRow(propType); if (!propertyRow) - propertyRow = createRow(OBJTYPE_UNKNOWN, masterRow, 0, propType, index); + propertyRow = createRow(OBJTYPE_UNKNOWN, masterRow, {}, propType, index); propertyRow->updateLock(masterRow->locked()); @@ -270,14 +270,14 @@ void RowManager::clearSelection() // set updateMaxDuration to false. void RowManager::updateRulerDuration(bool updateMaxDuration) { - double duration = 0; - double maxDuration = 0; // for setting correct size for the view so scrollbars appear correctly + long duration = 0; + long maxDuration = 0; // for setting correct size for the view so scrollbars appear correctly if (m_layoutTree->count() > 1) { auto rootRow = static_cast<RowTree *>(m_layoutTree->itemAt(1)->graphicsItem()); bool isComponent = rootRow->rowType() == OBJTYPE_COMPONENT; for (int i = 1; i < m_layoutTree->count(); ++i) { RowTree *row_i = static_cast<RowTree *>(m_layoutTree->itemAt(i)->graphicsItem()); - double dur_i = row_i->rowTimeline()->getEndTime(); + long dur_i = row_i->rowTimeline()->getEndTime(); if (((isComponent && i != 1) || row_i->rowType() == OBJTYPE_LAYER) && dur_i > duration) duration = dur_i; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h index ae5aa2cb..61525718 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineConstants.h @@ -36,7 +36,8 @@ namespace TimelineConstants const int ROW_H_EXPANDED = 120; // property rows height when graph is shown const int ROW_SPACING = 2; const int ROW_DEPTH_STEP = 15; // x-distance between 2 consecutive depths - const int RULER_SEC_W = 30; // width of 1 second section (at scale 1) + const double RULER_SEC_W = 30; // width of 1 second section (at scale 1) + const double RULER_MILLI_W = RULER_SEC_W / 1000.0; // width of 1 millisecond section at scale 1 const int RULER_SEC_DIV = 10; // second divisions const int RULER_DIV_H1 = 5; // height of main divisions const int RULER_DIV_H2 = 3; // height of secondary divisions @@ -69,7 +70,7 @@ namespace TimelineConstants const int AUTO_SCROLL_DELTA = 8; // increment in scroll at each time step const int AUTO_SCROLL_TRIGGER = 500; // time after which auto scroll starts (millis) const int AUTO_EXPAND_TIME = 500; // auto expand a hovered row (while DnD-ing) - const double MAX_SLIDE_TIME = 3599.0; // seconds + const long MAX_SLIDE_TIME = 3599000; // milliseconds const int TIMELINE_SCROLL_MAX_DELTA = 25; // Maximum amount of pixels to scroll per frame const int TIMELINE_SCROLL_DIVISOR = 6; // Divisor for timeline autoscroll distance diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.cpp index 03764b13..378b20da 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.cpp @@ -31,11 +31,8 @@ #include "RowManager.h" #include "RowTree.h" #include "Bindings/ITimelineItemBinding.h" -#include "DurationEditDlg.h" #include "StudioApp.h" #include "Dialogs.h" -#include "Core.h" -#include "Doc.h" TimelineControl::TimelineControl(TimelineGraphicsScene *scene) : m_scene(scene) @@ -54,29 +51,24 @@ void TimelineControl::setRowTimeline(RowTimeline *rowTimeline) void TimelineControl::showDurationEditDialog() { - g_StudioApp.GetDialogs()->asyncDisplayDurationEditDialog(m_startTime * 1000, m_endTime * 1000, - g_StudioApp.GetCore()->GetDoc(), this); + g_StudioApp.GetDialogs()->asyncDisplayDurationEditDialog(m_startTime, m_endTime, this); } void TimelineControl::ChangeStartTime(long inTime) { - double time = (double)inTime / 1000; - m_rowTimeline->setStartTime(time); + m_rowTimeline->setStartTime(inTime); } void TimelineControl::ChangeEndTime(long inTime) { - double time = (double)inTime / 1000; - m_rowTimeline->setEndTime(time); + m_rowTimeline->setEndTime(inTime); m_scene->rowManager()->updateRulerDuration(); } void TimelineControl::Commit() { - long startTime = m_rowTimeline->getStartTime() * 1000; - m_timebar->ChangeTime(startTime, true); - long endTime = m_rowTimeline->getEndTime() * 1000; - m_timebar->ChangeTime(endTime, false); + m_timebar->ChangeTime(m_rowTimeline->getStartTime(), true); + m_timebar->ChangeTime(m_rowTimeline->getEndTime(), false); m_timebar->CommitTimeChange(); } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.h index 981b6db7..3c348016 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineControl.h @@ -53,8 +53,8 @@ private: TimelineGraphicsScene *m_scene = nullptr; RowTimeline *m_rowTimeline = nullptr; ITimelineTimebar *m_timebar = nullptr; - double m_startTime = 0; - double m_endTime = 0; + long m_startTime = 0; + long m_endTime = 0; }; #endif // TIMELINECONTROL_H diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp index e8c6730a..67e1c897 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.cpp @@ -50,7 +50,7 @@ #include "RowTreeContextMenu.h" #include "RowTimelineContextMenu.h" #include "StudioPreferences.h" -#include "TimeEditDlg.h" +#include "TimeEnums.h" #include "StudioClipboard.h" #include "Dialogs.h" #include "Qt3DSDMStudioSystem.h" @@ -80,16 +80,15 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) , m_layoutTimeline(new QGraphicsLinearLayout(Qt::Vertical)) , m_ruler(new Ruler) , m_playHead(new PlayHead(m_ruler)) - , m_selectionRect(new SelectionRect()) - , m_rowMover(new RowMover(this)) , m_widgetTimeline(timelineWidget) , m_widgetRoot(new QGraphicsWidget) + , m_rowMover(new RowMover(this)) + , m_selectionRect(new SelectionRect()) , m_rowManager(new RowManager(this, m_layoutTree, m_layoutTimeline)) , m_keyframeManager(new KeyframeManager(this)) , m_pressPos(invalidPoint) , m_pressScreenPos(invalidPoint) , m_timelineControl(new TimelineControl(this)) - , m_currentCursor(-1) { addItem(m_playHead); addItem(m_selectionRect); @@ -164,8 +163,7 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) } if (m_selectionRect->isActive()) { - p -= QPoint(0, m_widgetTimeline->navigationBar()->height() - + TimelineConstants::ROW_H); + p -= QPoint(0, m_widgetTimeline->navigationBar()->height() + TimelineConstants::ROW_H); const double bottom = timelineContent->contentsRect().height() - scrollBarOffsets.y(); if (m_lastAutoScrollX != p.x() || p.x() <= 0 || p.x() >= right || m_lastAutoScrollY != p.y() || p.y() <= 0 || p.y() >= bottom) { @@ -200,7 +198,7 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) if (scroll != 0) scroll -= TimelineConstants::TREE_BOUND_W; - double distance = p.x() + scroll; + double distance = p.x() + scroll - TimelineConstants::RULER_EDGE_OFFSET; if (m_clickedTimelineControlType == TimelineControlType::Duration && !m_editedTimelineRow.isNull()) { distance -= m_editedTimelineRow->getDurationMoveOffsetX(); @@ -210,8 +208,7 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) snap(distance, !m_rulerPressed); if (m_rulerPressed) { - long time = m_ruler->distanceToTime( - distance - TimelineConstants::RULER_EDGE_OFFSET) * 1000; + long time = m_ruler->distanceToTime(distance); if (time < 0) time = 0; g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(time); @@ -223,22 +220,21 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) if (m_dragging) { if (m_clickedTimelineControlType == TimelineControlType::StartHandle) { - double visiblePtX = distance > TimelineConstants::RULER_EDGE_OFFSET - ? m_editedTimelineRow->getStartX() : 0; + double visiblePtX = distance > 0 ? m_editedTimelineRow->getStartX() : 0; if (distance > m_editedTimelineRow->getEndX()) visiblePtX += TimelineConstants::RULER_EDGE_OFFSET; m_editedTimelineRow->setStartX(distance); m_editedTimelineRow->showToolTip(QCursor::pos()); - timelineContent->ensureVisible(TimelineConstants::TREE_BOUND_W + visiblePtX, + timelineContent->ensureVisible(TimelineConstants::TREE_BOUND_W + + TimelineConstants::RULER_EDGE_OFFSET + + visiblePtX, m_editedTimelineRow->y(), 0, 0, 0, 0); } else if (m_clickedTimelineControlType == TimelineControlType::EndHandle) { - double time = m_ruler->distanceToTime( - distance - TimelineConstants::RULER_EDGE_OFFSET); + long time = m_ruler->distanceToTime(distance); double edgeMargin = 0; if (time > TimelineConstants::MAX_SLIDE_TIME) { - distance = m_ruler->timeToDistance(TimelineConstants::MAX_SLIDE_TIME) - + TimelineConstants::RULER_EDGE_OFFSET; + distance = m_ruler->timeToDistance(TimelineConstants::MAX_SLIDE_TIME); edgeMargin = TimelineConstants::RULER_EDGE_OFFSET; } else if (time < m_editedTimelineRow->getStartTime()) { edgeMargin = -TimelineConstants::RULER_EDGE_OFFSET; @@ -248,27 +244,27 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *timelineWidget) rowManager()->updateRulerDuration(p.x() > right); timelineContent->ensureVisible( TimelineConstants::TREE_BOUND_W + + TimelineConstants::RULER_EDGE_OFFSET + m_editedTimelineRow->getEndX() + edgeMargin, m_editedTimelineRow->y(), 0, 0, 0, 0); } else if (m_clickedTimelineControlType == TimelineControlType::Duration) { - double time = m_ruler->distanceToTime( - distance - TimelineConstants::RULER_EDGE_OFFSET) - + m_editedTimelineRow->getDuration(); // seconds + long time = m_ruler->distanceToTime(distance) + + m_editedTimelineRow->getDuration(); // milliseconds double visiblePtX = distance - + m_editedTimelineRow->getDurationMoveOffsetX(); + + m_editedTimelineRow->getDurationMoveOffsetX(); if (time > TimelineConstants::MAX_SLIDE_TIME) { distance = m_ruler->timeToDistance(TimelineConstants::MAX_SLIDE_TIME - - m_editedTimelineRow->getDuration()) - + TimelineConstants::RULER_EDGE_OFFSET; + - m_editedTimelineRow->getDuration()); visiblePtX = m_editedTimelineRow->getEndX() - + TimelineConstants::RULER_EDGE_OFFSET; + + TimelineConstants::RULER_EDGE_OFFSET; } m_editedTimelineRow->moveDurationTo(distance); m_editedTimelineRow->showToolTip(QCursor::pos()); rowManager()->updateRulerDuration(p.x() > right); timelineContent->ensureVisible( - TimelineConstants::TREE_BOUND_W + visiblePtX, + TimelineConstants::TREE_BOUND_W + + TimelineConstants::RULER_EDGE_OFFSET + visiblePtX, m_editedTimelineRow->y(), 0, 0, 0, 0); } } @@ -365,8 +361,8 @@ void TimelineGraphicsScene::setControllerText(const QString &controller) void TimelineGraphicsScene::updateTimelineLayoutWidth() { double timelineWidth = TimelineConstants::RULER_EDGE_OFFSET * 2 - + m_ruler->maxDuration() * TimelineConstants::RULER_SEC_W - * m_ruler->timelineScale(); + + m_ruler->maxDuration() * TimelineConstants::RULER_MILLI_W + * m_ruler->timelineScale(); m_layoutTimeline->setMinimumWidth(timelineWidth); m_layoutTimeline->setMaximumWidth(timelineWidth); @@ -547,12 +543,12 @@ void TimelineGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) m_pressPosInKeyframe = (m_pressPos.x() - m_ruler->x()) - (TimelineConstants::RULER_EDGE_OFFSET + m_ruler->timeToDistance(keyframe->time)); - m_keyframePressed = true; + m_pressedKeyframe = keyframe; } } else { m_keyframeManager->deselectAllKeyframes(); - m_clickedTimelineControlType = - m_editedTimelineRow->getClickedControl(m_pressPos); + m_clickedTimelineControlType + = m_editedTimelineRow->getClickedControl(m_pressPos); // clicked an empty spot on a timeline row, start selection rect. if (m_clickedTimelineControlType == TimelineControlType::None) { @@ -560,7 +556,7 @@ void TimelineGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event) } else if (m_clickedTimelineControlType == TimelineControlType::Duration) { if (!ctrlKeyDown && m_rowManager->isRowSelected(m_editedTimelineRow->rowTree()) - && !m_rowManager->isSingleSelected() ) { + && !m_rowManager->isSingleSelected()) { m_releaseSelectRow = m_editedTimelineRow->rowTree(); } @@ -631,16 +627,16 @@ void TimelineGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) m_rowMover->updateTargetRow(event->scenePos()); updateAutoScrolling(event->scenePos().y()); } - } else if (m_keyframePressed) { // moving selected keyframes - double newX = event->scenePos().x() - m_ruler->x() - m_pressPosInKeyframe; + } else if (m_pressedKeyframe) { // moving selected keyframes + double newX = event->scenePos().x() - m_ruler->x() + - TimelineConstants::RULER_EDGE_OFFSET - m_pressPosInKeyframe; - if (newX < TimelineConstants::RULER_EDGE_OFFSET) - newX = TimelineConstants::RULER_EDGE_OFFSET; + if (newX < 0) + newX = 0; if (shift) snap(newX); - newX += m_ruler->x() + m_pressPosInKeyframe; - double dx = newX - m_pressPos.x(); - m_keyframeManager->moveSelectedKeyframes(dx); + + m_keyframeManager->moveSelectedKeyframes(ruler()->distanceToTime(newX)); m_pressPos.setX(newX); } @@ -690,23 +686,23 @@ void TimelineGraphicsScene::stopAutoScroll() { void TimelineGraphicsScene::updateSnapSteps() { m_snapSteps.clear(); - // i = 1 is always the scene row + // i = 1 is always the scene row (or component root) for (int i = 2; i < m_layoutTimeline->count(); i++) { - RowTree *rowTree = static_cast<RowTree *> - (m_layoutTree->itemAt(i)->graphicsItem()); + RowTree *rowTree = static_cast<RowTree *>(m_layoutTree->itemAt(i)->graphicsItem()); if (rowTree->hasDurationBar() && rowTree->isVisible()) { - if (!m_snapSteps.contains(rowTree->rowTimeline()->getStartX())) - m_snapSteps.push_back(rowTree->rowTimeline()->getStartX()); + double startX = rowTree->rowTimeline()->getStartX(); + if (!m_snapSteps.contains(startX)) + m_snapSteps.push_back(startX); - if (!m_snapSteps.contains(rowTree->rowTimeline()->getEndX())) - m_snapSteps.push_back(rowTree->rowTimeline()->getEndX()); + double endX = rowTree->rowTimeline()->getEndX(); + if (!m_snapSteps.contains(endX)) + m_snapSteps.push_back(endX); // add keyframes times if (rowTree->hasPropertyChildren()) { const QList<Keyframe *> keyframes = rowTree->rowTimeline()->keyframes(); for (Keyframe *k : keyframes) { - double kX = m_ruler->timeToDistance(k->time) - + TimelineConstants::RULER_EDGE_OFFSET; + double kX = m_ruler->timeToDistance(k->time); if (!m_snapSteps.contains(kX)) m_snapSteps.push_back(kX); } @@ -730,7 +726,7 @@ void TimelineGraphicsScene::resetMousePressParams() m_timelinePanning = false; m_startRowMoverOnNextDrag = false; m_rulerPressed = false; - m_keyframePressed = false; + m_pressedKeyframe = nullptr; m_clickedTimelineControlType = TimelineControlType::None; m_editedTimelineRow.clear(); m_releaseSelectRow.clear(); @@ -743,6 +739,11 @@ void TimelineGraphicsScene::resetMousePressParams() m_lastAutoScrollY = -1.0; } +void TimelineGraphicsScene::resetPressedKeyframe() +{ + m_pressedKeyframe = nullptr; +} + QLabel *TimelineGraphicsScene::timebarTooltip() { return m_timebarToolTip; @@ -752,7 +753,8 @@ void TimelineGraphicsScene::snap(double &value, bool snapToPlayHead) { // snap to play head if (snapToPlayHead) { - double playHeadX = m_playHead->x() - m_ruler->x(); + double playHeadX = m_playHead->x() - TimelineConstants::TREE_BOUND_W + - TimelineConstants::RULER_EDGE_OFFSET; if (abs(value - playHeadX) < CStudioPreferences::GetSnapRange()) { value = playHeadX; return; @@ -775,8 +777,7 @@ void TimelineGraphicsScene::snap(double &value, bool snapToPlayHead) else if (CStudioPreferences::GetTimelineSnappingGridResolution() == SNAPGRID_TICKMARKS) snapStep *= .1; - double snapValue = TimelineConstants::RULER_EDGE_OFFSET - + round((value - TimelineConstants::RULER_EDGE_OFFSET) / snapStep) * snapStep; + double snapValue = round(value / snapStep) * snapStep; if (abs(value - snapValue) < CStudioPreferences::GetSnapRange()) value = snapValue; } @@ -788,37 +789,33 @@ void TimelineGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) if (m_dragging) { if (m_rowMover->isActive()) { // moving rows (reorder/reparent) commitMoveRows(); - } else if (m_keyframePressed) { + } else if (m_pressedKeyframe) { // update keyframe movement (time) to binding m_keyframeManager->commitMoveSelectedKeyframes(); } else if (m_clickedTimelineControlType == TimelineControlType::StartHandle) { if (!m_editedTimelineRow.isNull()) { ITimelineTimebar *timebar = m_editedTimelineRow->rowTree()->getBinding() ->GetTimelineItem()->GetTimebar(); - timebar->ChangeTime(m_editedTimelineRow->getStartTime() * 1000, true); + timebar->ChangeTime(m_editedTimelineRow->getStartTime(), true); timebar->CommitTimeChange(); } } else if (m_clickedTimelineControlType == TimelineControlType::EndHandle) { if (!m_editedTimelineRow.isNull()) { ITimelineTimebar *timebar = m_editedTimelineRow->rowTree()->getBinding() - ->GetTimelineItem()->GetTimebar(); - timebar->ChangeTime(m_editedTimelineRow->getEndTime() * 1000, false); + ->GetTimelineItem()->GetTimebar(); + timebar->ChangeTime(m_editedTimelineRow->getEndTime(), false); timebar->CommitTimeChange(); - if (m_playHead->time() > ruler()->duration()) { - g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(ruler()->duration() - * 1000); - } + if (m_playHead->time() > ruler()->duration()) + g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(ruler()->duration()); } } else if (m_clickedTimelineControlType == TimelineControlType::Duration) { if (!m_editedTimelineRow.isNull()) { ITimelineTimebar *timebar = m_editedTimelineRow->rowTree()->getBinding() ->GetTimelineItem()->GetTimebar(); - timebar->OffsetTime(m_editedTimelineRow->getDurationMoveTime() * 1000); + timebar->OffsetTime(m_editedTimelineRow->getDurationMoveTime()); timebar->CommitTimeChange(); - if (m_playHead->time() > ruler()->duration()) { - g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(ruler()->duration() - * 1000); - } + if (m_playHead->time() > ruler()->duration()) + g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(ruler()->duration()); } } } else if (!m_rulerPressed && (!m_releaseSelectRow.isNull() || !itemAt(event->scenePos(), @@ -849,7 +846,8 @@ void TimelineGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *even || itemBelowPlayhead->type() == TimelineItem::TypeRuler) { CDoc *doc = g_StudioApp.GetCore()->GetDoc(); g_StudioApp.GetDialogs()->asyncDisplayTimeEditDialog(doc->GetCurrentViewTime(), - doc, PLAYHEAD); + doc, PLAYHEAD, + m_keyframeManager); } else { item = itemBelowPlayhead; if (item->type() == TimelineItem::TypeRowTree) { @@ -880,8 +878,9 @@ void TimelineGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *even RowTimeline *rowTimeline = static_cast<RowTimeline *>(item); Keyframe *clickedKeyframe = rowTimeline->getClickedKeyframe(scenePos); if (clickedKeyframe) { + m_pressedKeyframe = clickedKeyframe; g_StudioApp.GetDialogs()->asyncDisplayTimeEditDialog( - clickedKeyframe->time * 1000, g_StudioApp.GetCore()->GetDoc(), + clickedKeyframe->time, g_StudioApp.GetCore()->GetDoc(), ASSETKEYFRAME, m_keyframeManager); } else { if (!rowTimeline->rowTree()->locked()) @@ -980,8 +979,7 @@ void TimelineGraphicsScene::updateHoverStatus(const QPointF &scenePos) item = getItemBelowType(TimelineItem::TypePlayHead, item, scenePos); if (item->type() == TimelineItem::TypeRowTimeline) { RowTimeline *timelineItem = static_cast<RowTimeline *>(item); - TimelineControlType controlType = - timelineItem->getClickedControl(scenePos); + TimelineControlType controlType = timelineItem->getClickedControl(scenePos); if (controlType == TimelineControlType::StartHandle || controlType == TimelineControlType::EndHandle) { setMouseCursor(CMouseCursor::CURSOR_RESIZE_LEFTRIGHT); @@ -1120,3 +1118,4 @@ KeyframeManager *TimelineGraphicsScene::keyframeManager() const { return m QGraphicsLinearLayout *TimelineGraphicsScene::layoutTree() const { return m_layoutTree; } QGraphicsLinearLayout *TimelineGraphicsScene::layoutTimeline() const { return m_layoutTimeline; } TimelineWidget *TimelineGraphicsScene::widgetTimeline() const { return m_widgetTimeline; } +Keyframe *TimelineGraphicsScene::pressedKeyframe() const { return m_pressedKeyframe; } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h index e8eda8f3..8a41ea49 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineGraphicsScene.h @@ -95,6 +95,8 @@ public: QPoint getScrollbarOffsets() const; void handleShowDISelector(const QString &propertyname, qt3dsdm::Qt3DSDMInstanceHandle inInst, const QPoint &pos); + void resetPressedKeyframe(); + Keyframe *pressedKeyframe() const; protected: bool event(QEvent *event) override; @@ -140,13 +142,13 @@ private: QPointF m_pressPos; QPointF m_pressScreenPos; QList<double> m_snapSteps; - CMouseCursor::Qt3DSMouseCursor m_currentCursor; + CMouseCursor::Qt3DSMouseCursor m_currentCursor = -1; TimelineControl *m_timelineControl = nullptr; DataInputSelectView *m_dataInputSelector = nullptr; // triggered by context menu but owned by // rowtree bool m_rulerPressed = false; - bool m_keyframePressed = false; + Keyframe *m_pressedKeyframe = nullptr; bool m_dragging = false; bool m_startRowMoverOnNextDrag = false; bool m_timelineZooming = false; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.cpp index 6240fc34..73f5e155 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.cpp @@ -52,12 +52,12 @@ #include "Bindings/Qt3DSDMTimelineItemBinding.h" #include "Bindings/Qt3DSDMTimelineItemProperty.h" #include "Bindings/TimelineBreadCrumbProvider.h" -#include "TimeEditDlg.h" #include "IDocumentEditor.h" #include "Control.h" #include "TimelineDropTarget.h" #include "StudioPreferences.h" #include "Dialogs.h" +#include "TimeEnums.h" #include <QtGui/qevent.h> #include <QtWidgets/qgraphicslinearlayout.h> @@ -84,7 +84,7 @@ public: }; TimelineWidget::TimelineWidget(const QSize &preferredSize, QWidget *parent) - : QWidget() + : QWidget(parent) , m_toolbar(new TimelineToolbar()) , m_viewTreeHeader(new TreeHeaderView(this)) , m_viewTreeContent(new QGraphicsView(this)) @@ -247,17 +247,18 @@ TimelineWidget::TimelineWidget(const QSize &preferredSize, QWidget *parent) connect(m_toolbar, &TimelineToolbar::deleteLayerTriggered, [=](){ doc->DeleteSelectedObject(); }); - connect(m_toolbar, &TimelineToolbar::gotoTimeTriggered, this, [this]() { + connect(m_toolbar, &TimelineToolbar::gotoTimeTriggered, this, [=]() { CDoc *doc = g_StudioApp.GetCore()->GetDoc(); g_StudioApp.GetDialogs()->asyncDisplayTimeEditDialog(doc->GetCurrentViewTime(), - doc, PLAYHEAD); + doc, PLAYHEAD, + m_graphicsScene->keyframeManager()); }); - connect(m_toolbar, &TimelineToolbar::firstFrameTriggered, this, [this]() { + connect(m_toolbar, &TimelineToolbar::firstFrameTriggered, this, []() { g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(0); }); - connect(m_toolbar, &TimelineToolbar::stopTriggered, this, [this]() { + connect(m_toolbar, &TimelineToolbar::stopTriggered, this, []() { g_StudioApp.PlaybackStopNoRestore(); }); @@ -270,7 +271,7 @@ TimelineWidget::TimelineWidget(const QSize &preferredSize, QWidget *parent) }); connect(m_toolbar, &TimelineToolbar::lastFrameTriggered, this, [this]() { - double dur = m_graphicsScene->ruler()->duration() * 1000; + long dur = m_graphicsScene->ruler()->duration(); g_StudioApp.GetCore()->GetDoc()->NotifyTimeChanged(dur); }); @@ -305,15 +306,13 @@ Q3DStudio::CString TimelineWidget::getPlaybackMode() CDoc *doc = g_StudioApp.GetCore()->GetDoc(); qt3dsdm::Qt3DSDMSlideHandle theActiveSlide(doc->GetActiveSlide()); // clock has passed the end, check whether needs to switch slide - qt3dsdm::Qt3DSDMInstanceHandle theInstanceHandle = doc->GetStudioSystem()->GetSlideSystem() - ->GetSlideInstance(theActiveSlide); + qt3dsdm::Qt3DSDMInstanceHandle instance = doc->GetStudioSystem()->GetSlideSystem() + ->GetSlideInstance(theActiveSlide); - CClientDataModelBridge *clientDataModelBridge = doc->GetStudioSystem() - ->GetClientDataModelBridge(); + CClientDataModelBridge *bridge = doc->GetStudioSystem()->GetClientDataModelBridge(); qt3dsdm::IPropertySystem *propertySystem = doc->GetStudioSystem()->GetPropertySystem(); qt3dsdm::SValue theValue; - propertySystem->GetInstancePropertyValue(theInstanceHandle, clientDataModelBridge->GetSlide() - .m_PlayMode, theValue); + propertySystem->GetInstancePropertyValue(instance, bridge->GetSlide().m_PlayMode, theValue); return qt3dsdm::get<qt3dsdm::TDataStrPtr>(theValue)->GetData(); } @@ -377,7 +376,7 @@ void TimelineWidget::OnNewPresentation() std::placeholders::_1, std::placeholders::_2))); m_connections.push_back(theSignalProvider->ConnectFirstKeyframeDynamicSet( std::bind(&TimelineWidget::onFirstKeyframeDynamicSet, this, - std::placeholders::_1, std::placeholders::_2))); + std::placeholders::_1))); // action created/deleted m_connections.push_back(theSignalProvider->ConnectActionCreated( @@ -426,7 +425,7 @@ void TimelineWidget::OnClosingPresentation() void TimelineWidget::OnTimeChanged(long inTime) { - m_graphicsScene->playHead()->setTime(inTime * .001); + m_graphicsScene->playHead()->setTime(inTime); m_toolbar->setTime(inTime); double left = m_viewTimelineHeader->horizontalScrollBar()->value() @@ -474,6 +473,7 @@ void TimelineWidget::insertToHandlesMapRecursive(Qt3DSDMTimelineItemBinding *bin return; insertToHandlesMap(binding); + const QList<ITimelineItemBinding *> children = binding->GetChildren(); for (auto child : children) insertToHandlesMapRecursive(static_cast<Qt3DSDMTimelineItemBinding *>(child)); @@ -596,8 +596,7 @@ void TimelineWidget::onAnimationCreated(qt3dsdm::Qt3DSDMInstanceHandle parentIns for (int i = 0; i < propBinding->GetKeyframeCount(); i++) { IKeyframe *kf = propBinding->GetKeyframeByIndex(i); Keyframe *kfUI = m_graphicsScene->keyframeManager()->insertKeyframe( - propRow->rowTimeline(), static_cast<double>(kf->GetTime()) * .001, false) - .at(0); + propRow->rowTimeline(), kf->GetTime(), false).at(0); kf->setUI(kfUI); kfUI->binding = static_cast<Qt3DSDMTimelineKeyframe *>(kf); @@ -685,10 +684,8 @@ void TimelineWidget::refreshKeyframe(qt3dsdm::Qt3DSDMAnimationHandle inAnimation } } -void TimelineWidget::onFirstKeyframeDynamicSet(qt3dsdm::Qt3DSDMAnimationHandle inAnimation, - bool inDynamic) +void TimelineWidget::onFirstKeyframeDynamicSet(qt3dsdm::Qt3DSDMAnimationHandle inAnimation) { - Q_UNUSED(inDynamic) refreshKeyframe(inAnimation, 0, ETimelineKeyframeTransaction_DynamicChanged); } @@ -755,6 +752,28 @@ void TimelineWidget::onPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance m_subpresentationChanges.insert(inInstance); if (!m_asyncUpdateTimer.isActive()) m_asyncUpdateTimer.start(); + } else if (inProperty == m_bridge->GetLayer().m_variants) { + qt3dsdm::SValue sValue; + if (doc->GetPropertySystem()->GetInstancePropertyValue(inInstance, inProperty, sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + if (!propVal.isEmpty()) { + QStringList tagPairs = propVal.split(QLatin1Char(',')); + QStringList groups; + for (int i = 0; i < tagPairs.size(); ++i) { + QString group = tagPairs[i].left(tagPairs[i].indexOf(QLatin1Char(':'))); + if (!groups.contains(group)) + groups.append(group); + } + + m_variantsMap[inInstance] = groups; + } else { + m_variantsMap[inInstance].clear(); + } + + if (!m_asyncUpdateTimer.isActive()) + m_asyncUpdateTimer.start(); + } } } @@ -778,11 +797,11 @@ void TimelineWidget::onAsyncUpdate() m_graphicsScene->updateController(); onSelectionChange(doc->GetSelectedValue()); m_toolbar->setNewLayerEnabled(!m_graphicsScene->rowManager()->isComponentRoot()); + refreshVariants(); - // update suppresentation indicators - for (auto *row : m_handlesMap) + // update sub-presentation indicators + for (auto *row : qAsConst(m_handlesMap)) row->updateSubpresentations(); - } else { if (!m_moveMap.isEmpty()) { // Flip the hash around so that we collect moves by parent. @@ -926,10 +945,22 @@ void TimelineWidget::onAsyncUpdate() } m_graphicsScene->updateSnapSteps(); } + + if (!m_variantsMap.isEmpty()) { + const auto instances = m_variantsMap.keys(); + for (int instance : instances) { + if (m_handlesMap.contains(instance)) { + RowTree *row = m_handlesMap[instance]; + if (row) + row->updateVariants(m_variantsMap[instance]); // variants groups names + } + } + } } m_dirtyProperties.clear(); m_moveMap.clear(); m_actionChanges.clear(); + m_variantsMap.clear(); m_subpresentationChanges.clear(); m_keyframeChangesMap.clear(); m_graphicsScene->rowManager()->finalizeRowDeletions(); @@ -977,8 +1008,7 @@ void TimelineWidget::onPropertyLinked(qt3dsdm::Qt3DSDMInstanceHandle inInstance, for (int i = 0; i < propBinding->GetKeyframeCount(); i++) { IKeyframe *kf = propBinding->GetKeyframeByIndex(i); Keyframe *kfUI = m_graphicsScene->keyframeManager()->insertKeyframe( - propRow->rowTimeline(), static_cast<double>(kf->GetTime()) * .001, - false).at(0); + propRow->rowTimeline(), kf->GetTime(), false).at(0); kf->setUI(kfUI); kfUI->binding = static_cast<Qt3DSDMTimelineKeyframe *>(kf); @@ -1030,21 +1060,6 @@ void TimelineWidget::onChildMoved(int inParent, int inChild, long inOldIndex, onChildAdded(inParent, inChild, inNewIndex); } -void TimelineWidget::OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation) -{ - -} - -void TimelineWidget::Draw(CRenderer *inRenderer) -{ - -} - -void TimelineWidget::OnGainFocus() -{ - -} - CDropTarget *TimelineWidget::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, EStudioObjectType objectType, Q3DStudio::DocumentEditorFileType::Enum fileType) @@ -1079,12 +1094,6 @@ CDropTarget *TimelineWidget::FindDropCandidate(CPt &inMousePoint, Qt::KeyboardMo return theTarget; } -bool TimelineWidget::OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags) -{ - return true; -} - - void TimelineWidget::OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) { Q_UNUSED(inFlags) @@ -1103,14 +1112,6 @@ bool TimelineWidget::blockMousePress() const return m_blockMousePress; } -void TimelineWidget::OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) -{ -} - -void TimelineWidget::OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) -{ -} - CPt TimelineWidget::GetPreferredSize() { return CPt(m_preferredSize.width(), m_preferredSize.height()); @@ -1150,7 +1151,7 @@ Qt3DSDMTimelineItemBinding *TimelineWidget::getBindingForHandle(int handle, const QList<ITimelineItemBinding *> children = binding->GetChildren(); for (auto child : children) { Qt3DSDMTimelineItemBinding *b = getBindingForHandle(handle, - static_cast<Qt3DSDMTimelineItemBinding *>(child)); + static_cast<Qt3DSDMTimelineItemBinding *>(child)); if (b) return b; @@ -1174,6 +1175,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { + Q_UNUSED(event) m_splitterPressed = false; } @@ -1232,8 +1234,8 @@ void TimelineWidget::onTimeBarColorChanged(const QColor &color) void TimelineWidget::setSelectedTimeBarsColor(const QColor &color, bool preview) { using namespace Q3DStudio; // Needed for SCOPED_DOCUMENT_EDITOR macro - auto rows = selectedRows(); - for (RowTree *row : qAsConst(rows)) { + const auto rows = selectedRows(); + for (RowTree *row : rows) { row->rowTimeline()->setBarColor(color); if (!preview) { Qt3DSDMTimelineItemBinding *timelineItemBinding = @@ -1244,3 +1246,33 @@ void TimelineWidget::setSelectedTimeBarsColor(const QColor &color, bool preview) } } } + +void TimelineWidget::refreshVariants() +{ + const auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetPropertySystem(); + const auto layers = g_StudioApp.GetCore()->GetDoc()->getLayers(); + for (auto layer : layers) { + if (!m_handlesMap.contains(layer)) + continue; + + qt3dsdm::SValue sValue; + if (propertySystem->GetInstancePropertyValue(layer, m_bridge->GetLayer().m_variants, + sValue)) { + QString propVal = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>(sValue) + ->GetData()); + if (!propVal.isEmpty()) { + QStringList tagPairs = propVal.split(QLatin1Char(',')); + QStringList groups; + for (int i = 0; i < tagPairs.size(); ++i) { + QString group = tagPairs[i].left(tagPairs[i].indexOf(QLatin1Char(':'))); + if (!groups.contains(group)) + groups.append(group); + } + + m_handlesMap[layer]->updateVariants(groups); + } else { + m_handlesMap[layer]->updateVariants({}); + } + } + } +} diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h index 26c9a66b..ac01367a 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/TimelineWidget.h @@ -73,6 +73,7 @@ public: void openBarColorDialog(); void onTimeBarColorChanged(const QColor &color); void setSelectedTimeBarsColor(const QColor &color, bool preview); + void refreshVariants(); void enableDnD(bool b = true); bool dndActive() const; bool blockMousePress() const; @@ -87,16 +88,10 @@ public: bool hasSelectedKeyframes() const; // CControl - void OnDraw(CRenderer *inRenderer, CRct &inDirtyRect, bool inIgnoreValidation = false) override; - void Draw(CRenderer *inRenderer) override; - void OnGainFocus() override; CDropTarget *FindDropCandidate(CPt &inMousePoint, Qt::KeyboardModifiers inFlags, EStudioObjectType objectType, Q3DStudio::DocumentEditorFileType::Enum fileType) override; - bool OnMouseHover(CPt inPoint, Qt::KeyboardModifiers inFlags) override; void OnMouseMove(CPt inPoint, Qt::KeyboardModifiers inFlags) override; - void OnMouseOut(CPt inPoint, Qt::KeyboardModifiers inFlags) override; - void OnMouseUp(CPt inPoint, Qt::KeyboardModifiers inFlags) override; CPt GetPreferredSize() override; void SetSize(long inX, long inY) override; bool isFullReconstructPending() const { return m_fullReconstruct; } @@ -115,7 +110,7 @@ protected: void onKeyframeDeleted(qt3dsdm::Qt3DSDMAnimationHandle inAnimation, qt3dsdm::Qt3DSDMKeyframeHandle inKeyframe); void onKeyframeUpdated(qt3dsdm::Qt3DSDMKeyframeHandle inKeyframe); - void onFirstKeyframeDynamicSet(qt3dsdm::Qt3DSDMAnimationHandle inAnimation, bool inDynamic); + void onFirstKeyframeDynamicSet(qt3dsdm::Qt3DSDMAnimationHandle inAnimation); void onAnimationDeleted(qt3dsdm::Qt3DSDMInstanceHandle parentInstance, qt3dsdm::Qt3DSDMPropertyHandle property); void onActionEvent(qt3dsdm::Qt3DSDMActionHandle inAction, qt3dsdm::Qt3DSDMSlideHandle inSlide, @@ -164,6 +159,7 @@ private: QSize m_preferredSize; QMultiHash<qt3dsdm::Qt3DSDMInstanceHandle, qt3dsdm::Qt3DSDMPropertyHandle> m_dirtyProperties; QHash<int, int> m_moveMap; // key: child handle, value: parent handle + QHash<int, QStringList> m_variantsMap; // key: obj handle, value: variant groups QSet<int> m_actionChanges; // key: object handle QSet<int> m_subpresentationChanges; // key: object handle QMultiHash<int, int> m_keyframeChangesMap; // key: object handle, value: property handle diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.cpp index 35787462..3ddfdcad 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.cpp @@ -30,9 +30,11 @@ #include "Ruler.h" #include "TimelineConstants.h" #include "StudioPreferences.h" +#include "StudioUtils.h" #include <QtGui/qpainter.h> #include <QtGui/qcursor.h> +#include <QtWidgets/qwidget.h> PlayHead::PlayHead(Ruler *ruler) : QGraphicsRectItem() @@ -47,24 +49,22 @@ void PlayHead::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q_UNUSED(option) Q_UNUSED(widget) + bool hiResIcons = StudioUtils::devicePixelRatio(widget->window()->windowHandle()) > 1.0; static const QPixmap pixHead = QPixmap(":/images/PlaybackHead.png"); + static const QPixmap pixHead2x = QPixmap(":/images/PlaybackHead@2x.png"); static const int PLAY_HEAD_H = 999999; // theoretically big enough height - painter->drawPixmap(-TimelineConstants::PLAYHEAD_W * .5, 0, pixHead); + painter->drawPixmap(-TimelineConstants::PLAYHEAD_W * .5, 0, hiResIcons ? pixHead2x : pixHead); painter->setPen(CStudioPreferences::timelinePlayheadLineColor()); painter->drawLine(0, 0, 0, PLAY_HEAD_H); } void PlayHead::setHeight(int height) { - setRect( - rect().x(), - rect().y(), - rect().width(), - height); + setRect(rect().x(), rect().y(), rect().width(), height); } -void PlayHead::setTime(double time) +void PlayHead::setTime(long time) { if (time < 0) time = 0; @@ -78,21 +78,21 @@ void PlayHead::setTime(double time) void PlayHead::setPosition(double posX) { posX = qBound(TimelineConstants::RULER_EDGE_OFFSET, posX, m_ruler->duration() - * TimelineConstants::RULER_SEC_W * m_ruler->timelineScale() + * TimelineConstants::RULER_MILLI_W * m_ruler->timelineScale() + TimelineConstants::RULER_EDGE_OFFSET); setX(m_ruler->x() + posX); m_time = (posX - TimelineConstants::RULER_EDGE_OFFSET) - / (TimelineConstants::RULER_SEC_W * m_ruler->timelineScale()); + / (TimelineConstants::RULER_MILLI_W * m_ruler->timelineScale()); } void PlayHead::updatePosition() { setX(m_ruler->x() + TimelineConstants::RULER_EDGE_OFFSET - + m_time * TimelineConstants::RULER_SEC_W * m_ruler->timelineScale()); + + m_time * TimelineConstants::RULER_MILLI_W * m_ruler->timelineScale()); } -double PlayHead::time() const +long PlayHead::time() const { return m_time; } diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.h index ef628f51..395e6317 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/PlayHead.h @@ -44,14 +44,14 @@ public: void setHeight(int height); void setPosition(double posX); // set x poisiotn void updatePosition(); // sync x poisiotn based on time value - void setTime(double time); // set time (sets x based on time input) + void setTime(long time); // set time (sets x based on time (ms) input) void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - double time() const; - int type() const; + long time() const; + int type() const override; private: - double m_time = 0; + long m_time = 0; Ruler *m_ruler; }; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.cpp index 196cedde..a0b689f4 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.cpp @@ -41,6 +41,7 @@ #include "AppFonts.h" #include "StudioPreferences.h" #include "TimelineToolbar.h" +#include "StudioUtils.h" #include <QtGui/qpainter.h> #include <QtGui/qbrush.h> @@ -101,6 +102,8 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio { Q_UNUSED(option) + bool hiResIcons = StudioUtils::devicePixelRatio(widget->window()->windowHandle()) > 1.0; + if (!y()) // prevents flickering when the row is just inserted to the layout return; @@ -122,6 +125,8 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio painter->fillRect(0, 0, size().width(), currentHeight, bgColor); } + const double edgeOffset = TimelineConstants::RULER_EDGE_OFFSET; + // Duration. Draw duration bar (for scene/component root) also if it has // datainput controller if (m_rowTree->hasDurationBar() || m_controllerDataInput.size()) { @@ -132,17 +137,17 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio painter->setBrush(QBrush(CStudioPreferences::timelineRowColorDurationOff1(), Qt::BDiagPattern)); painter->setPen(Qt::NoPen); - painter->fillRect(QRect(m_startX, 0, m_endX - m_startX, currentHeight), + painter->fillRect(QRect(edgeOffset + m_startX, 0, m_endX - m_startX, currentHeight), CStudioPreferences::timelineRowColorDurationOff2()); - painter->drawRect(QRect(m_startX, 0, m_endX - m_startX, currentHeight)); + painter->drawRect(QRect(edgeOffset + m_startX, 0, m_endX - m_startX, currentHeight)); painter->setPen(QPen(CStudioPreferences::timelineRowColorDurationEdge(), 2)); - painter->drawLine(m_startX, 0, m_startX, currentHeight); - painter->drawLine(m_endX, 0, m_endX, currentHeight); + painter->drawLine(edgeOffset + m_startX, 0, edgeOffset + m_startX, currentHeight); + painter->drawLine(edgeOffset + m_endX, 0, edgeOffset + m_endX, currentHeight); } else { // draw main duration part - double x = qMax(m_startX, m_minStartX); - double w = qMin(m_endX, m_maxEndX) - x; + double x = edgeOffset + qMax(m_startX, m_minStartX); + double w = edgeOffset + qMin(m_endX, m_maxEndX) - x; static const int marginY = 3; painter->setPen(Qt::NoPen); @@ -161,8 +166,9 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio } if (m_controllerDataInput.size()) { - static const QPixmap pixDataInput - = QPixmap(":/images/Objects-DataInput-White.png"); + static const QPixmap pixDataInput = QPixmap(":/images/Objects-DataInput-White.png"); + static const QPixmap pixDataInput2x + = QPixmap(":/images/Objects-DataInput-White@2x.png"); static const QFontMetrics fm(painter->font()); // need clip region to limit datainput icon visibility to the same rect as we use @@ -178,8 +184,7 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio int iconx = x + (w - textwidth) / 2; if (iconx < x) iconx = x; - painter->drawPixmap(iconx, marginY, pixDataInput.width(), pixDataInput.height(), - pixDataInput); + painter->drawPixmap(iconx, marginY, hiResIcons ? pixDataInput2x : pixDataInput); painter->setPen(Qt::NoPen); painter->setClipping(false); } @@ -189,37 +194,40 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio Qt::BDiagPattern)); if (m_startX < m_minStartX) { painter->setPen(Qt::NoPen); - painter->fillRect(QRect(m_startX, 0, m_minStartX - m_startX, currentHeight), + painter->fillRect(QRect(edgeOffset + m_startX, 0, m_minStartX - m_startX, + currentHeight), CStudioPreferences::timelineRowColorDurationOff2()); - painter->drawRect(QRect(m_startX, 0, m_minStartX - m_startX, currentHeight)); + painter->drawRect(QRect(edgeOffset + m_startX, 0, m_minStartX - m_startX, + currentHeight)); painter->setPen(CStudioPreferences::timelineRowColorDurationEdge()); - painter->drawLine(m_minStartX, 0, m_minStartX, currentHeight); + painter->drawLine(edgeOffset + m_minStartX, 0, edgeOffset + m_minStartX, + currentHeight); } // draw hashed part after if (m_endX > m_maxEndX) { painter->setPen(Qt::NoPen); - painter->fillRect(QRect(m_maxEndX, 0, m_endX - m_maxEndX, currentHeight), + painter->fillRect(QRect(edgeOffset + m_maxEndX, 0, m_endX - m_maxEndX, + currentHeight), CStudioPreferences::timelineRowColorDurationOff2()); - painter->drawRect(QRect(m_maxEndX, 0, m_endX - m_maxEndX, currentHeight)); + painter->drawRect(QRect(edgeOffset + m_maxEndX, 0, m_endX - m_maxEndX, + currentHeight)); painter->setPen(CStudioPreferences::timelineRowColorDurationEdge()); - painter->drawLine(m_maxEndX, 0, m_maxEndX, currentHeight); + painter->drawLine(edgeOffset + m_maxEndX, 0, edgeOffset + m_maxEndX, currentHeight); } if (m_rowTree->indexInLayout() != 1) { painter->setPen(QPen(CStudioPreferences::timelineRowColorDurationEdge(), 2)); - painter->drawLine(m_startX, 0, m_startX, currentHeight); - painter->drawLine(m_endX, 0, m_endX, currentHeight); + painter->drawLine(edgeOffset + m_startX, 0, edgeOffset + m_startX, currentHeight); + painter->drawLine(edgeOffset + m_endX, 0, edgeOffset + m_endX, currentHeight); } } painter->restore(); } - if (m_propertyGraph) { - // Property graph - QRectF graphRect(TimelineConstants::RULER_EDGE_OFFSET, 0, - widget->width(), currentHeight); + if (m_propertyGraph) { // Property graph + QRectF graphRect(edgeOffset, 0, widget->width(), currentHeight); m_propertyGraph->paintGraphs(painter, graphRect); } @@ -228,23 +236,28 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio const qreal keyFrameHalfH = keyFrameH / 2.0; const qreal keyFrameY = (qMin(currentHeight, TimelineConstants::ROW_H) / 2.0) - keyFrameHalfH; const qreal hiddenKeyFrameY = keyFrameY + (keyFrameH * 2.0 / 3.0) + 2.0; + const qreal keyFrameOffset = hiResIcons ? 8 : 7.5; // Hidden descendant keyframe indicators if (!m_rowTree->expanded()) { static const QPixmap pixKeyframeHidden = QPixmap(":/images/keyframe-hidden-normal.png"); - QVector<double> childKeyframeTimes; + static const QPixmap pixKeyframeHidden2x + = QPixmap(":/images/keyframe-hidden-normal@2x.png"); + QVector<long> childKeyframeTimes; collectChildKeyframeTimes(childKeyframeTimes); const qreal oldOpacity = painter->opacity(); painter->setOpacity(0.75); for (const auto time : qAsConst(childKeyframeTimes)) { - const qreal xCoord = timeToX(time) - 2.5; - painter->drawPixmap(QPointF(xCoord, hiddenKeyFrameY), pixKeyframeHidden); + const qreal xCoord = edgeOffset + m_rowTree->m_scene->ruler()->timeToDistance(time) + - 2.5; + painter->drawPixmap(QPointF(xCoord, hiddenKeyFrameY), hiResIcons ? pixKeyframeHidden2x + : pixKeyframeHidden); } painter->setOpacity(oldOpacity); } - if (m_rowTree->hasPropertyChildren()) { // master keyframes + if (m_rowTree->hasPropertyChildren()) { // object row keyframes static const QPixmap pixKeyframeMasterDisabled = QPixmap(":/images/Keyframe-Master-Disabled.png"); static const QPixmap pixKeyframeMasterNormal @@ -257,27 +270,60 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio = QPixmap(":/images/Keyframe-MasterDynamic-Normal.png"); static const QPixmap pixKeyframeMasterDynamicSelected = QPixmap(":/images/Keyframe-MasterDynamic-Selected.png"); + static const QPixmap pixKeyframeMasterDisabled2x + = QPixmap(":/images/Keyframe-Master-Disabled@2x.png"); + static const QPixmap pixKeyframeMasterNormal2x + = QPixmap(":/images/Keyframe-Master-Normal@2x.png"); + static const QPixmap pixKeyframeMasterSelected2x + = QPixmap(":/images/Keyframe-Master-Selected@2x.png"); + static const QPixmap pixKeyframeMasterDynamicDisabled2x + = QPixmap(":/images/Keyframe-MasterDynamic-Disabled@2x.png"); + static const QPixmap pixKeyframeMasterDynamicNormal2x + = QPixmap(":/images/Keyframe-MasterDynamic-Normal@2x.png"); + static const QPixmap pixKeyframeMasterDynamicSelected2x + = QPixmap(":/images/Keyframe-MasterDynamic-Selected@2x.png"); for (auto keyframe : qAsConst(m_keyframes)) { QPixmap pixmap; if (m_rowTree->locked()) { - if (keyframe->dynamic) - pixmap = pixKeyframeMasterDynamicDisabled; - else - pixmap = pixKeyframeMasterDisabled; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframeMasterDynamicDisabled2x + : pixKeyframeMasterDynamicDisabled; + } else { + pixmap = hiResIcons ? pixKeyframeMasterDisabled2x + : pixKeyframeMasterDisabled; + } } else if (keyframe->selected()) { - if (keyframe->dynamic) - pixmap = pixKeyframeMasterDynamicSelected; - else - pixmap = pixKeyframeMasterSelected; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframeMasterDynamicSelected2x + : pixKeyframeMasterDynamicSelected; + } else { + pixmap = hiResIcons ? pixKeyframeMasterSelected2x + : pixKeyframeMasterSelected; + } } else { - if (keyframe->dynamic) - pixmap = pixKeyframeMasterDynamicNormal; - else - pixmap = pixKeyframeMasterNormal; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframeMasterDynamicNormal2x + : pixKeyframeMasterDynamicNormal; + } else { + pixmap = hiResIcons ? pixKeyframeMasterNormal2x + : pixKeyframeMasterNormal; + } + } + painter->drawPixmap(QPointF(edgeOffset + m_rowTree->m_scene->ruler() + ->timeToDistance(keyframe->time) - keyFrameOffset, + keyFrameY), pixmap); + + // highlight the pressed keyframe in a multi-selection (the keyframe that is affected + // by snapping, and setting time dialog) + if (m_rowTree->m_scene->keyframeManager()->selectedKeyframes().size() > 1 + && m_rowTree->m_scene->pressedKeyframe() == keyframe) { + painter->setPen(QPen(CStudioPreferences::timelinePressedKeyframeColor(), 1)); + painter->drawArc(edgeOffset + m_rowTree->m_scene->ruler() + ->timeToDistance(keyframe->time) - 4, keyFrameY + 4, 9, 9, 0, + 5760); } - painter->drawPixmap(QPointF(timeToX(keyframe->time) - 8.5, keyFrameY), pixmap); } - } else if (m_rowTree->isProperty()) { + } else if (m_rowTree->isProperty()) { // property row keyframes static const QPixmap pixKeyframePropertyDisabled = QPixmap(":/images/Keyframe-Property-Disabled.png"); static const QPixmap pixKeyframePropertyNormal @@ -290,26 +336,51 @@ void RowTimeline::paint(QPainter *painter, const QStyleOptionGraphicsItem *optio = QPixmap(":/images/Keyframe-PropertyDynamic-Normal.png"); static const QPixmap pixKeyframePropertyDynamicSelected = QPixmap(":/images/Keyframe-PropertyDynamic-Selected.png"); + static const QPixmap pixKeyframePropertyDisabled2x + = QPixmap(":/images/Keyframe-Property-Disabled@2x.png"); + static const QPixmap pixKeyframePropertyNormal2x + = QPixmap(":/images/Keyframe-Property-Normal@2x.png"); + static const QPixmap pixKeyframePropertySelected2x + = QPixmap(":/images/Keyframe-Property-Selected@2x.png"); + static const QPixmap pixKeyframePropertyDynamicDisabled2x + = QPixmap(":/images/Keyframe-PropertyDynamic-Disabled@2x.png"); + static const QPixmap pixKeyframePropertyDynamicNormal2x + = QPixmap(":/images/Keyframe-PropertyDynamic-Normal@2x.png"); + static const QPixmap pixKeyframePropertyDynamicSelected2x + = QPixmap(":/images/Keyframe-PropertyDynamic-Selected@2x.png"); for (auto keyframe : qAsConst(m_keyframes)) { QPixmap pixmap; if (m_rowTree->locked()) { - if (keyframe->dynamic) - pixmap = pixKeyframePropertyDynamicDisabled; - else - pixmap = pixKeyframePropertyDisabled; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframePropertyDynamicDisabled2x + : pixKeyframePropertyDynamicDisabled; + + } else { + pixmap = hiResIcons ? pixKeyframePropertyDisabled2x + : pixKeyframePropertyDisabled; + } } else if (keyframe->selected()) { - if (keyframe->dynamic) - pixmap = pixKeyframePropertyDynamicSelected; - else - pixmap = pixKeyframePropertySelected; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframePropertyDynamicSelected2x + : pixKeyframePropertyDynamicSelected; + + } else { + pixmap = hiResIcons ? pixKeyframePropertySelected2x + : pixKeyframePropertySelected; + } } else { - if (keyframe->dynamic) - pixmap = pixKeyframePropertyDynamicNormal; - else - pixmap = pixKeyframePropertyNormal; + if (keyframe->dynamic) { + pixmap = hiResIcons ? pixKeyframePropertyDynamicNormal2x + : pixKeyframePropertyDynamicNormal; + + } else { + pixmap = hiResIcons ? pixKeyframePropertyNormal2x + : pixKeyframePropertyNormal; + } } - painter->drawPixmap(QPointF(timeToX(keyframe->time) - (keyframe->selected() ? 7.5 : 5.5), - keyFrameY), pixmap); + painter->drawPixmap(QPointF(edgeOffset + m_rowTree->m_scene->ruler() + ->timeToDistance(keyframe->time) - keyFrameOffset, + keyFrameY), pixmap); } } } @@ -340,15 +411,14 @@ void RowTimeline::drawColorPropertyGradient(QPainter *painter, int width) QLinearGradient bgGradient(0, 0, width, 0); for (auto keyframe : qAsConst(m_keyframes)) { - double xPos = timeToX(keyframe->time); + double xPos = m_rowTree->m_scene->ruler()->timeToDistance(keyframe->time); double gradPos = xPos / width; gradPos = qBound(0.0, gradPos, 1.0); - long timeMs = keyframe->time * 1000; QColor currentColor; // Get the color at the specified time. - currentColor.setRed(propBinding->GetChannelValueAtTime(0, timeMs)); - currentColor.setGreen(propBinding->GetChannelValueAtTime(1, timeMs)); - currentColor.setBlue(propBinding->GetChannelValueAtTime(2, timeMs)); + currentColor.setRed(propBinding->GetChannelValueAtTime(0, keyframe->time)); + currentColor.setGreen(propBinding->GetChannelValueAtTime(1, keyframe->time)); + currentColor.setBlue(propBinding->GetChannelValueAtTime(2, keyframe->time)); bgGradient.setColorAt(gradPos, currentColor); } painter->fillRect(TimelineConstants::RULER_EDGE_OFFSET, 0, @@ -373,7 +443,8 @@ Keyframe *RowTimeline::getClickedKeyframe(const QPointF &scenePos) } for (const auto keyframe : qAsConst(keyframes)) { - x = timeToX(keyframe->time); + x = TimelineConstants::RULER_EDGE_OFFSET + + m_rowTree->m_scene->ruler()->timeToDistance(keyframe->time); if (p.x() > x - 5 && p.x() < x + 5 && p.y() > 3 && p.y() < 16) return keyframe; @@ -391,7 +462,8 @@ QList<Keyframe *> RowTimeline::getKeyframesInRange(const QRectF &rect) const static const int KF_CENTER_Y = 10; for (auto keyframe : qAsConst(m_keyframes)) { - x = timeToX(keyframe->time); + x = TimelineConstants::RULER_EDGE_OFFSET + + m_rowTree->m_scene->ruler()->timeToDistance(keyframe->time); if (localRect.left() < x && localRect.right() > x && localRect.top() < KF_CENTER_Y && localRect.bottom() > KF_CENTER_Y) { @@ -409,8 +481,8 @@ void RowTimeline::updateDurationFromBinding() ITimelineTimebar *timebar = m_rowTree->m_binding->GetTimelineItem()->GetTimebar(); clearBoundChildren(); - setStartTime(timebar->GetStartTime() * .001); - setEndTime(timebar->GetEndTime() * .001); + setStartTime(timebar->GetStartTime()); + setEndTime(timebar->GetEndTime()); } void RowTimeline::updateKeyframesFromBinding(const QList<int> &properties) @@ -430,8 +502,7 @@ void RowTimeline::updateKeyframesFromBinding(const QList<int> &properties) Qt3DSDMTimelineKeyframe *kf = static_cast<Qt3DSDMTimelineKeyframe *> (child->m_PropBinding->GetKeyframeByIndex(i)); - Keyframe *kfUI = new Keyframe(static_cast<double>(kf->GetTime() * .001), - child->rowTimeline()); + Keyframe *kfUI = new Keyframe(kf->GetTime(), child->rowTimeline()); kfUI->binding = kf; kfUI->dynamic = kf->IsDynamic(); kf->setUI(kfUI); @@ -504,10 +575,12 @@ TimelineControlType RowTimeline::getClickedControl(const QPointF &scenePos) cons if (!m_rowTree->locked()) { QPointF p = mapFromScene(scenePos.x(), scenePos.y()); + p.setX(p.x() - TimelineConstants::RULER_EDGE_OFFSET); + const int halfHandle = TimelineConstants::DURATION_HANDLE_W * .5; // Never choose start handle if end time is zero, as you cannot adjust it in that case bool startHandle = p.x() > m_startX - halfHandle && p.x() < m_startX + halfHandle - && !qFuzzyIsNull(m_endTime); + && m_endTime > 0; bool endHandle = p.x() > m_endX - halfHandle && p.x() < m_endX + halfHandle; if (startHandle && endHandle) { // If handles overlap, choose the handle based on the side of the click relative to start @@ -566,8 +639,8 @@ void RowTimeline::clearBoundChildren() // move the duration area (start/end x) void RowTimeline::moveDurationBy(double dx) { - if (m_startX + dx < TimelineConstants::RULER_EDGE_OFFSET) - dx = TimelineConstants::RULER_EDGE_OFFSET - m_startX; + if (m_startX + dx < 0) + dx = -m_startX; m_startX += dx; m_endX += dx; @@ -578,8 +651,9 @@ void RowTimeline::moveDurationBy(double dx) m_maxEndX = m_endX; } - m_startTime = xToTime(m_startX); - m_endTime = xToTime(m_endX); + Ruler *ruler = m_rowTree->m_scene->ruler(); + m_startTime = ruler->distanceToTime(m_startX); + m_endTime = ruler->distanceToTime(m_endX); // move keyframes with the row if (!m_rowTree->isProperty()) { // make sure we don't move the keyframes twice @@ -602,8 +676,8 @@ void RowTimeline::moveDurationBy(double dx) void RowTimeline::moveDurationTo(double newX) { - if (newX < TimelineConstants::RULER_EDGE_OFFSET) - newX = TimelineConstants::RULER_EDGE_OFFSET; + if (newX < 0) + newX = 0; double dx = newX - m_startX; double durationX = m_endX - m_startX; @@ -617,13 +691,14 @@ void RowTimeline::moveDurationTo(double newX) m_maxEndX = m_endX; } - m_startTime = xToTime(m_startX); - m_endTime = xToTime(m_endX); + Ruler *ruler = m_rowTree->m_scene->ruler(); + m_startTime = ruler->distanceToTime(m_startX); + m_endTime = ruler->distanceToTime(m_endX); // move keyframes with the row if (!m_rowTree->isProperty()) { // make sure we don't move the keyframes twice for (Keyframe *keyframe : qAsConst(m_keyframes)) - keyframe->time += rowTree()->m_scene->ruler()->distanceToTime(dx); + keyframe->time += ruler->distanceToTime(dx); } update(); @@ -639,7 +714,7 @@ void RowTimeline::moveDurationTo(double newX) } } -double RowTimeline::getDurationMoveTime() const +long RowTimeline::getDurationMoveTime() const { return m_startTime - m_startDurationMoveStartTime; } @@ -649,26 +724,12 @@ double RowTimeline::getDurationMoveOffsetX() const return m_startDurationMoveOffsetX; } -double RowTimeline::getDuration() const +long RowTimeline::getDuration() const { return m_endTime - m_startTime; } -// convert time (seconds) values to x -double RowTimeline::timeToX(double time) const -{ - return TimelineConstants::RULER_EDGE_OFFSET + time * TimelineConstants::RULER_SEC_W - * rowTree()->m_scene->ruler()->timelineScale(); -} - -// convert x values to time (seconds) -double RowTimeline::xToTime(double xPos) const -{ - return (xPos - TimelineConstants::RULER_EDGE_OFFSET) - / (TimelineConstants::RULER_SEC_W * rowTree()->m_scene->ruler()->timelineScale()); -} - -void RowTimeline::collectChildKeyframeTimes(QVector<double> &childKeyframeTimes) +void RowTimeline::collectChildKeyframeTimes(QVector<long> &childKeyframeTimes) { const auto childRows = m_rowTree->childRows(); for (const auto row : childRows) { @@ -690,13 +751,13 @@ void RowTimeline::updatePosition() // Set the position of the start of the row duration void RowTimeline::setStartX(double startX) { - if (startX < TimelineConstants::RULER_EDGE_OFFSET) - startX = TimelineConstants::RULER_EDGE_OFFSET; + if (startX < 0) + startX = 0; else if (startX > m_endX) startX = m_endX; m_startX = startX; - m_startTime = xToTime(startX); + m_startTime = m_rowTree->m_scene->ruler()->distanceToTime(startX); if (!m_rowTree->parentRow() || m_rowTree->parentRow()->rowType() == OBJTYPE_SCENE || m_rowTree->hasComponentAncestor()) { @@ -715,7 +776,7 @@ void RowTimeline::setEndX(double endX) endX = m_startX; m_endX = endX; - m_endTime = xToTime(endX); + m_endTime = m_rowTree->m_scene->ruler()->distanceToTime(endX); if (!m_rowTree->parentRow() || m_rowTree->parentRow()->rowType() == OBJTYPE_SCENE || m_rowTree->hasComponentAncestor()) { @@ -832,10 +893,10 @@ void RowTimeline::updateCommentItemPos() -TimelineConstants::ROW_TEXT_OFFSET_Y); } -void RowTimeline::setStartTime(double startTime) +void RowTimeline::setStartTime(long startTime) { m_startTime = startTime; - m_startX = timeToX(startTime); + m_startX = m_rowTree->m_scene->ruler()->timeToDistance(startTime); if (!m_rowTree->parentRow() || m_rowTree->parentRow()->rowType() == OBJTYPE_SCENE || m_rowTree->hasComponentAncestor()) { @@ -847,10 +908,10 @@ void RowTimeline::setStartTime(double startTime) update(); } -void RowTimeline::setEndTime(double endTime) +void RowTimeline::setEndTime(long endTime) { m_endTime = endTime; - m_endX = timeToX(endTime); + m_endX = m_rowTree->m_scene->ruler()->timeToDistance(endTime); if (!m_rowTree->parentRow() || m_rowTree->parentRow()->rowType() == OBJTYPE_SCENE || m_rowTree->hasComponentAncestor()) { @@ -862,22 +923,24 @@ void RowTimeline::setEndTime(double endTime) update(); } +// duration start x in local space (x=0 at time=0) double RowTimeline::getStartX() const { return m_startX; } +// duration end x in local space double RowTimeline::getEndX() const { return m_endX; } -double RowTimeline::getStartTime() const +long RowTimeline::getStartTime() const { return m_startTime; } -double RowTimeline::getEndTime() const +long RowTimeline::getEndTime() const { return m_endTime; } @@ -912,16 +975,16 @@ QList<Keyframe *> RowTimeline::keyframes() const return m_keyframes; } -QString RowTimeline::formatTime(double seconds) const +QString RowTimeline::formatTime(long millis) const { static const QString timeTemplate = tr("%1:%2.%3"); static const QChar fillChar = tr("0").at(0); - long mins = seconds / 60; - long secs = seconds - mins * 60; - long millis = qRound((seconds - (int)seconds) * 1000); + long mins = millis % 3600000 / 60000; + long secs = millis % 60000 / 1000; + long mils = millis % 1000; - return timeTemplate.arg(mins).arg(secs, 2, 10, fillChar).arg(millis, 3, 10, fillChar); + return timeTemplate.arg(mins).arg(secs, 2, 10, fillChar).arg(mils, 3, 10, fillChar); } void RowTimeline::showToolTip(const QPointF &pos) diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.h index 32dab1bc..00c81696 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimeline.h @@ -57,8 +57,8 @@ public: void clearBoundChildren(); void moveDurationBy(double dx); void moveDurationTo(double newX); - void setStartTime(double startTime); - void setEndTime(double endTime); + void setStartTime(long startTime); + void setEndTime(long endTime); void setStartX(double startX); void setEndX(double endX); void setBarColor(const QColor &color); @@ -72,13 +72,13 @@ public: TimelineControlType getClickedControl(const QPointF &scenePos) const; double getStartX() const; double getEndX() const; - double getStartTime() const; - double getEndTime() const; - double getDurationMoveTime() const; // the time a row duration has moved (to commit to binding) + long getStartTime() const; + long getEndTime() const; + long getDurationMoveTime() const; // the time a row duration has moved (to commit to binding) double getDurationMoveOffsetX() const; - double getDuration() const; + long getDuration() const; QColor barColor() const; - int type() const; + int type() const override; RowTimeline *parentRow() const; RowTree *rowTree() const; Keyframe *getClickedKeyframe(const QPointF &scenePos); @@ -99,18 +99,16 @@ private: void updateCommentItemPos(); void drawColorPropertyGradient(QPainter *painter, int width); bool isColorProperty() const; - QString formatTime(double seconds) const; - double timeToX(double time) const; - double xToTime(double xPos) const; - void collectChildKeyframeTimes(QVector<double> &childKeyframeTimes); + QString formatTime(long millis) const; + void collectChildKeyframeTimes(QVector<long> &childKeyframeTimes); RowTree *m_rowTree; RowTimelinePropertyGraph *m_propertyGraph = nullptr; RowTimelineCommentItem *m_commentItem = nullptr; - double m_startTime = 0; - double m_startDurationMoveStartTime = 0; + long m_startTime = 0; + long m_startDurationMoveStartTime = 0; double m_startDurationMoveOffsetX = 0; - double m_endTime = 0; + long m_endTime = 0; double m_startX = 0; double m_endX = 0; double m_minStartX = 0; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelineContextMenu.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelineContextMenu.cpp index ecd31b9a..7861bb19 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelineContextMenu.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelineContextMenu.cpp @@ -232,7 +232,7 @@ void RowTimelineContextMenu::setInterpolation() void RowTimelineContextMenu::setKeyframeTime() { - m_keyframeManager->SetKeyframeTime(m_keyframe->time * 1000.0); + m_keyframeManager->SetKeyframeTime(m_keyframe->time); } void RowTimelineContextMenu::changeTimeBarColor() diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp index a65e235d..3c82d73b 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTimelinePropertyGraph.cpp @@ -85,7 +85,7 @@ void RowTimelinePropertyGraph::paintSingleChannel(QPainter *painter, long inChan QPainterPath path; for (int i = 0; i < m_rect.width(); i += interval) { // Value time in ms - long time = 1000 * (i / (TimelineConstants::RULER_SEC_W * timelineScale)); + long time = i / (TimelineConstants::RULER_MILLI_W * timelineScale); float value = m_propBinding->GetChannelValueAtTime(inChannelIndex, time); float yPos = graphY + (1.0 - (value - minVal) / (maxVal - minVal)) * graphHeight; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.cpp index 0056d90e..db313ca9 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.cpp @@ -38,6 +38,12 @@ #include "TreeHeader.h" #include "StudioPreferences.h" #include "KeyframeManager.h" +#include "StudioApp.h" +#include "Core.h" +#include "Doc.h" +#include "Qt3DSDMStudioSystem.h" +#include "Qt3DSDMSlides.h" +#include "StudioUtils.h" #include <QtGui/qpainter.h> #include "QtGui/qtextcursor.h" @@ -53,6 +59,9 @@ RowTree::RowTree(TimelineGraphicsScene *timelineScene, EStudioObjectType rowType m_scene = timelineScene; m_rowType = rowType; m_label = label; + CDoc *doc = g_StudioApp.GetCore()->GetDoc(); + m_onMasterSlide = doc->GetStudioSystem()->GetSlideSystem()->IsMasterSlide( + doc->GetActiveSlide()); initialize(); } @@ -173,6 +182,8 @@ void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q Q_UNUSED(option) Q_UNUSED(widget) + bool hiResIcons = StudioUtils::devicePixelRatio(widget->window()->windowHandle()) > 1.0; + if (!y()) // prevents flickering when the row is just inserted to the layout return; @@ -220,24 +231,45 @@ void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q static const QPixmap pixEmpty = QPixmap(":/images/Toggle-Empty.png"); static const QPixmap pixShy = QPixmap(":/images/Toggle-Shy.png"); static const QPixmap pixHide = QPixmap(":/images/Toggle-HideShow.png"); + static const QPixmap pixHideDisabled = QPixmap(":/images/Toggle-HideShow-disabled.png"); static const QPixmap pixHideCtrld = QPixmap(":/images/Toggle-HideShowControlled.png"); static const QPixmap pixLock = QPixmap(":/images/Toggle-Lock.png"); + static const QPixmap pixEmpty2x = QPixmap(":/images/Toggle-Empty@2x.png"); + static const QPixmap pixShy2x = QPixmap(":/images/Toggle-Shy@2x.png"); + static const QPixmap pixHide2x = QPixmap(":/images/Toggle-HideShow@2x.png"); + static const QPixmap pixHideDisabled2x = QPixmap(":/images/Toggle-HideShow-disabled@2x.png"); + static const QPixmap pixHideCtrld2x = QPixmap(":/images/Toggle-HideShowControlled@2x.png"); + static const QPixmap pixLock2x = QPixmap(":/images/Toggle-Lock@2x.png"); if (hasActionButtons()) { - painter->drawPixmap(m_rectShy , m_shy ? pixShy : pixEmpty); + painter->drawPixmap(m_rectShy, hiResIcons ? (m_shy ? pixShy2x : pixEmpty2x) + : (m_shy ? pixShy : pixEmpty)); // Eyeball visibility follows the visibility setting for the object even if it has // datainput controller - if (m_visibilityCtrld) - painter->drawPixmap(m_rectVisible, m_visible ? pixHideCtrld : pixEmpty); - else - painter->drawPixmap(m_rectVisible, m_visible ? pixHide : pixEmpty); - painter->drawPixmap(m_rectLocked , m_locked ? pixLock : pixEmpty); + // Disable eyeball from master slide + if (m_onMasterSlide) { + painter->drawPixmap(m_rectVisible, hiResIcons ? pixHideDisabled2x + : pixHideDisabled); + } else if (m_visibilityCtrld) { + painter->drawPixmap(m_rectVisible, hiResIcons + ? (m_visible ? pixHideCtrld2x : pixEmpty2x) + : (m_visible ? pixHideCtrld : pixEmpty)); + } else { + painter->drawPixmap(m_rectVisible, hiResIcons + ? (m_visible ? pixHide2x : pixEmpty2x) + : (m_visible ? pixHide : pixEmpty)); + } + painter->drawPixmap(m_rectLocked, hiResIcons ? (m_locked ? pixLock2x : pixEmpty2x) + : (m_locked ? pixLock : pixEmpty)); } static const QPixmap pixInsertLeft = QPixmap(":/images/Insert-Left.png"); static const QPixmap pixInsertRight = QPixmap(":/images/Insert-Right.png"); + static const QPixmap pixInsertLeft2x = QPixmap(":/images/Insert-Left@2x.png"); + static const QPixmap pixInsertRight2x = QPixmap(":/images/Insert-Right@2x.png"); if (m_dndState == DnDState::SP_TARGET) { // Candidate target of a subpresentation drop - painter->drawPixmap(19, 2, pixInsertLeft); - painter->drawPixmap(treeWidth() - TimelineConstants::TREE_ICONS_W - 8, 2, pixInsertRight); + painter->drawPixmap(19, 2, hiResIcons ? pixInsertLeft2x : pixInsertLeft); + painter->drawPixmap(treeWidth() - TimelineConstants::TREE_ICONS_W - 8, 2, hiResIcons + ? pixInsertRight2x : pixInsertRight); } else if (m_dndState == DnDState::Parent) { // Candidate parent of a dragged row painter->setPen(QPen(CStudioPreferences::timelineRowMoverColor(), 1)); painter->drawRect(QRect(1, 1, treeWidth() - 2, size().height() - 3)); @@ -250,6 +282,14 @@ void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q static const QPixmap pixChildAction = QPixmap(":/images/Action-ChildAction.png"); static const QPixmap pixCompMasterAction = QPixmap(":/images/Action-ComponentMasterAction.png"); static const QPixmap pixCompAction = QPixmap(":/images/Action-ComponentAction.png"); + static const QPixmap pixMasterAction2x = QPixmap(":/images/Action-MasterAction@2x.png"); + static const QPixmap pixAction2x = QPixmap(":/images/Action-Action@2x.png"); + static const QPixmap pixChildMasterAction2x + = QPixmap(":/images/Action-ChildMasterAction@2x.png"); + static const QPixmap pixChildAction2x = QPixmap(":/images/Action-ChildAction@2x.png"); + static const QPixmap pixCompMasterAction2x + = QPixmap(":/images/Action-ComponentMasterAction@2x.png"); + static const QPixmap pixCompAction2x = QPixmap(":/images/Action-ComponentAction@2x.png"); if (!isProperty()) { // subpresentation indicators @@ -262,116 +302,185 @@ void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q } if (m_actionStates & ActionState::MasterAction) // has master action - painter->drawPixmap(0, 0, pixMasterAction); + painter->drawPixmap(0, 0, hiResIcons ? pixMasterAction2x : pixMasterAction); else if (m_actionStates & ActionState::Action) // has action - painter->drawPixmap(0, 0, pixAction); + painter->drawPixmap(0, 0, hiResIcons ? pixAction2x : pixAction); if (!expanded()) { - if (m_actionStates & ActionState::MasterChildAction) // children have master action - painter->drawPixmap(0, 0, pixChildMasterAction); - else if (m_actionStates & ActionState::ChildAction) // children have action - painter->drawPixmap(0, 0, pixChildAction); + if (m_actionStates & ActionState::MasterChildAction) { + // children have master action + painter->drawPixmap(0, 0, hiResIcons ? pixChildMasterAction2x + : pixChildMasterAction); + } else if (m_actionStates & ActionState::ChildAction) { + // children have action + painter->drawPixmap(0, 0, hiResIcons ? pixChildAction2x : pixChildAction); + } } if (m_actionStates & ActionState::MasterComponentAction) // component has master action - painter->drawPixmap(0, 0, pixCompMasterAction); + painter->drawPixmap(0, 0, hiResIcons ? pixCompMasterAction2x : pixCompMasterAction); else if (m_actionStates & ActionState::ComponentAction) // component has action - painter->drawPixmap(0, 0, pixCompAction); + painter->drawPixmap(0, 0, hiResIcons ? pixCompAction2x : pixCompAction); + } + + // variants indicator + if (m_variantsGroups.size() > 0) { + const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef(); + for (int i = 0; i < m_variantsGroups.size(); ++i) { + painter->fillRect(QRect(clipX() + 2 + i * 9, 6, 6, 6), + variantsDef[m_variantsGroups[i]].m_color); + } } // The following items need to be clipped so that they do not draw overlapping shy etc. buttons - painter->setClipRect(0, 0, treeWidth() - TimelineConstants::TREE_ICONS_W, - TimelineConstants::ROW_H); + painter->setClipRect(0, 0, clipX(), TimelineConstants::ROW_H); // expand/collapse arrow static const QPixmap pixArrow = QPixmap(":/images/arrow.png"); static const QPixmap pixArrowDown = QPixmap(":/images/arrow_down.png"); - if (m_arrowVisible) - painter->drawPixmap(m_rectArrow, expanded() ? pixArrowDown : pixArrow); + static const QPixmap pixArrow2x = QPixmap(":/images/arrow@2x.png"); + static const QPixmap pixArrowDown2x = QPixmap(":/images/arrow_down@2x.png"); + if (m_arrowVisible) { + painter->drawPixmap(m_rectArrow, hiResIcons ? (expanded() ? pixArrowDown2x : pixArrow2x) + : (expanded() ? pixArrowDown : pixArrow)); + } // Row type icon - static const QPixmap pixSceneNormal = QPixmap(":/images/Objects-Scene-Normal.png"); - static const QPixmap pixLayerNormal = QPixmap(":/images/Objects-Layer-Normal.png"); - static const QPixmap pixObjectNormal = QPixmap(":/images/Objects-Model-Normal.png"); - static const QPixmap pixLightNormal = QPixmap(":/images/Objects-Light-Normal.png"); - static const QPixmap pixCameraNormal = QPixmap(":/images/Objects-Camera-Normal.png"); - static const QPixmap pixTextNormal = QPixmap(":/images/Objects-Text-Normal.png"); - static const QPixmap pixAliasNormal = QPixmap(":/images/Objects-Alias-Normal.png"); - static const QPixmap pixGroupNormal = QPixmap(":/images/Objects-Group-Normal.png"); + static const QPixmap pixSceneNormal = QPixmap(":/images/Objects-Scene-Normal.png"); + static const QPixmap pixLayerNormal = QPixmap(":/images/Objects-Layer-Normal.png"); + static const QPixmap pixObjectNormal = QPixmap(":/images/Objects-Model-Normal.png"); + static const QPixmap pixLightNormal = QPixmap(":/images/Objects-Light-Normal.png"); + static const QPixmap pixCameraNormal = QPixmap(":/images/Objects-Camera-Normal.png"); + static const QPixmap pixTextNormal = QPixmap(":/images/Objects-Text-Normal.png"); + static const QPixmap pixAliasNormal = QPixmap(":/images/Objects-Alias-Normal.png"); + static const QPixmap pixGroupNormal = QPixmap(":/images/Objects-Group-Normal.png"); static const QPixmap pixComponentNormal = QPixmap(":/images/Objects-Component-Normal.png"); - static const QPixmap pixMaterialNormal = QPixmap(":/images/Objects-Material-Normal.png"); - static const QPixmap pixPropertyNormal = QPixmap(":/images/Objects-Property-Normal.png"); - static const QPixmap pixImageNormal = QPixmap(":/images/Objects-Image-Normal.png"); - static const QPixmap pixBehaviorNormal = QPixmap(":/images/Objects-Behavior-Normal.png"); - static const QPixmap pixEffectNormal = QPixmap(":/images/Objects-Effect-Normal.png"); - - static const QPixmap pixSceneDisabled = QPixmap(":/images/Objects-Scene-Disabled.png"); - static const QPixmap pixLayerDisabled = QPixmap(":/images/Objects-Layer-Disabled.png"); - static const QPixmap pixObjectDisabled = QPixmap(":/images/Objects-Model-Disabled.png"); - static const QPixmap pixLightDisabled = QPixmap(":/images/Objects-Light-Disabled.png"); - static const QPixmap pixCameraDisabled = QPixmap(":/images/Objects-Camera-Disabled.png"); - static const QPixmap pixTextDisabled = QPixmap(":/images/Objects-Text-Disabled.png"); - static const QPixmap pixAliasDisabled = QPixmap(":/images/Objects-Alias-Disabled.png"); - static const QPixmap pixGroupDisabled = QPixmap(":/images/Objects-Group-Disabled.png"); + static const QPixmap pixMaterialNormal = QPixmap(":/images/Objects-Material-Normal.png"); + static const QPixmap pixPropertyNormal = QPixmap(":/images/Objects-Property-Normal.png"); + static const QPixmap pixImageNormal = QPixmap(":/images/Objects-Image-Normal.png"); + static const QPixmap pixBehaviorNormal = QPixmap(":/images/Objects-Behavior-Normal.png"); + static const QPixmap pixEffectNormal= QPixmap(":/images/Objects-Effect-Normal.png"); + static const QPixmap pixSceneNormal2x = QPixmap(":/images/Objects-Scene-Normal@2x.png"); + static const QPixmap pixLayerNormal2x = QPixmap(":/images/Objects-Layer-Normal@2x.png"); + static const QPixmap pixObjectNormal2x = QPixmap(":/images/Objects-Model-Normal@2x.png"); + static const QPixmap pixLightNormal2x = QPixmap(":/images/Objects-Light-Normal@2x.png"); + static const QPixmap pixCameraNormal2x = QPixmap(":/images/Objects-Camera-Normal@2x.png"); + static const QPixmap pixTextNormal2x = QPixmap(":/images/Objects-Text-Normal@2x.png"); + static const QPixmap pixAliasNormal2x = QPixmap(":/images/Objects-Alias-Normal@2x.png"); + static const QPixmap pixGroupNormal2x = QPixmap(":/images/Objects-Group-Normal@2x.png"); + static const QPixmap pixComponentNormal2x = QPixmap(":/images/Objects-Component-Normal@2x.png"); + static const QPixmap pixMaterialNormal2x = QPixmap(":/images/Objects-Material-Normal@2x.png"); + static const QPixmap pixPropertyNormal2x = QPixmap(":/images/Objects-Property-Normal@2x.png"); + static const QPixmap pixImageNormal2x = QPixmap(":/images/Objects-Image-Normal@2x.png"); + static const QPixmap pixBehaviorNormal2x = QPixmap(":/images/Objects-Behavior-Normal@2x.png"); + static const QPixmap pixEffectNormal2x = QPixmap(":/images/Objects-Effect-Normal@2x.png"); + + static const QPixmap pixSceneDisabled = QPixmap(":/images/Objects-Scene-Disabled.png"); + static const QPixmap pixLayerDisabled = QPixmap(":/images/Objects-Layer-Disabled.png"); + static const QPixmap pixObjectDisabled = QPixmap(":/images/Objects-Model-Disabled.png"); + static const QPixmap pixLightDisabled = QPixmap(":/images/Objects-Light-Disabled.png"); + static const QPixmap pixCameraDisabled = QPixmap(":/images/Objects-Camera-Disabled.png"); + static const QPixmap pixTextDisabled = QPixmap(":/images/Objects-Text-Disabled.png"); + static const QPixmap pixAliasDisabled = QPixmap(":/images/Objects-Alias-Disabled.png"); + static const QPixmap pixGroupDisabled = QPixmap(":/images/Objects-Group-Disabled.png"); static const QPixmap pixComponentDisabled = QPixmap(":/images/Objects-Component-Disabled.png"); - static const QPixmap pixMaterialDisabled = QPixmap(":/images/Objects-Material-Disabled.png"); - static const QPixmap pixPropertyDisabled = QPixmap(":/images/Objects-Property-Disabled.png"); - static const QPixmap pixImageDisabled = QPixmap(":/images/Objects-Image-Disabled.png"); - static const QPixmap pixBehaviorDisabled = QPixmap(":/images/Objects-Behavior-Disabled.png"); - static const QPixmap pixEffectDisabled = QPixmap(":/images/Objects-Effect-Disabled.png"); + static const QPixmap pixMaterialDisabled = QPixmap(":/images/Objects-Material-Disabled.png"); + static const QPixmap pixPropertyDisabled = QPixmap(":/images/Objects-Property-Disabled.png"); + static const QPixmap pixImageDisabled = QPixmap(":/images/Objects-Image-Disabled.png"); + static const QPixmap pixBehaviorDisabled = QPixmap(":/images/Objects-Behavior-Disabled.png"); + static const QPixmap pixEffectDisabled = QPixmap(":/images/Objects-Effect-Disabled.png"); + static const QPixmap pixSceneDisabled2x = QPixmap(":/images/Objects-Scene-Disabled@2x.png"); + static const QPixmap pixLayerDisabled2x = QPixmap(":/images/Objects-Layer-Disabled@2x.png"); + static const QPixmap pixObjectDisabled2x = QPixmap(":/images/Objects-Model-Disabled@2x.png"); + static const QPixmap pixLightDisabled2x = QPixmap(":/images/Objects-Light-Disabled@2x.png"); + static const QPixmap pixCameraDisabled2x = QPixmap(":/images/Objects-Camera-Disabled@2x.png"); + static const QPixmap pixTextDisabled2x = QPixmap(":/images/Objects-Text-Disabled@2x.png"); + static const QPixmap pixAliasDisabled2x = QPixmap(":/images/Objects-Alias-Disabled@2x.png"); + static const QPixmap pixGroupDisabled2x = QPixmap(":/images/Objects-Group-Disabled@2x.png"); + static const QPixmap pixComponentDisabled2x + = QPixmap(":/images/Objects-Component-Disabled@2x.png"); + static const QPixmap pixMaterialDisabled2x + = QPixmap(":/images/Objects-Material-Disabled@2x.png"); + static const QPixmap pixPropertyDisabled2x + = QPixmap(":/images/Objects-Property-Disabled@2x.png"); + static const QPixmap pixImageDisabled2x = QPixmap(":/images/Objects-Image-Disabled@2x.png"); + static const QPixmap pixBehaviorDisabled2x + = QPixmap(":/images/Objects-Behavior-Disabled@2x.png"); + static const QPixmap pixEffectDisabled2x = QPixmap(":/images/Objects-Effect-Disabled@2x.png"); QPixmap pixRowType; - switch (m_rowType) { - case OBJTYPE_SCENE: - pixRowType = m_locked ? pixSceneDisabled : pixSceneNormal; - break; - case OBJTYPE_LAYER: - pixRowType = m_locked ? pixLayerDisabled : pixLayerNormal; - break; - case OBJTYPE_MODEL: - pixRowType = m_locked ? pixObjectDisabled : pixObjectNormal; - break; - case OBJTYPE_LIGHT: - pixRowType = m_locked ? pixLightDisabled : pixLightNormal; - break; - case OBJTYPE_CAMERA: - pixRowType = m_locked ? pixCameraDisabled : pixCameraNormal; - break; - case OBJTYPE_TEXT: - pixRowType = m_locked ? pixTextDisabled : pixTextNormal; - break; - case OBJTYPE_ALIAS: - pixRowType = m_locked ? pixAliasDisabled : pixAliasNormal; - break; - case OBJTYPE_GROUP: - pixRowType = m_locked ? pixGroupDisabled : pixGroupNormal; - break; - case OBJTYPE_COMPONENT: - pixRowType = m_locked ? pixComponentDisabled : pixComponentNormal; - break; - case OBJTYPE_MATERIAL: - pixRowType = m_locked ? pixMaterialDisabled : pixMaterialNormal; - break; - case OBJTYPE_IMAGE: - pixRowType = m_locked ? pixImageDisabled : pixImageNormal; - break; - case OBJTYPE_BEHAVIOR: - pixRowType = m_locked ? pixBehaviorDisabled : pixBehaviorNormal; - break; - case OBJTYPE_EFFECT: - pixRowType = m_locked ? pixEffectDisabled : pixEffectNormal; - break; - default: - break; + if (m_isProperty) { + pixRowType = hiResIcons ? (m_locked ? pixPropertyDisabled2x : pixPropertyNormal2x) + : (m_locked ? pixPropertyDisabled : pixPropertyNormal); + } else { + switch (m_rowType) { + case OBJTYPE_SCENE: + pixRowType = hiResIcons ? (m_locked ? pixSceneDisabled2x : pixSceneNormal2x) + : (m_locked ? pixSceneDisabled : pixSceneNormal); + break; + case OBJTYPE_LAYER: + pixRowType = hiResIcons ? (m_locked ? pixLayerDisabled2x : pixLayerNormal2x) + : (m_locked ? pixLayerDisabled : pixLayerNormal); + break; + case OBJTYPE_MODEL: + pixRowType = hiResIcons ? (m_locked ? pixObjectDisabled2x : pixObjectNormal2x) + : (m_locked ? pixObjectDisabled : pixObjectNormal); + break; + case OBJTYPE_LIGHT: + pixRowType = hiResIcons ? (m_locked ? pixLightDisabled2x : pixLightNormal2x) + : (m_locked ? pixLightDisabled : pixLightNormal); + break; + case OBJTYPE_CAMERA: + pixRowType = hiResIcons ? (m_locked ? pixCameraDisabled2x : pixCameraNormal2x) + : (m_locked ? pixCameraDisabled : pixCameraNormal); + break; + case OBJTYPE_TEXT: + pixRowType = hiResIcons ? (m_locked ? pixTextDisabled2x : pixTextNormal2x) + : (m_locked ? pixTextDisabled : pixTextNormal); + break; + case OBJTYPE_ALIAS: + pixRowType = hiResIcons ? (m_locked ? pixAliasDisabled2x : pixAliasNormal2x) + : (m_locked ? pixAliasDisabled : pixAliasNormal); + break; + case OBJTYPE_GROUP: + pixRowType = hiResIcons ? (m_locked ? pixGroupDisabled2x : pixGroupNormal2x) + : (m_locked ? pixGroupDisabled : pixGroupNormal); + break; + case OBJTYPE_COMPONENT: + pixRowType = hiResIcons ? (m_locked ? pixComponentDisabled2x : pixComponentNormal2x) + : (m_locked ? pixComponentDisabled : pixComponentNormal); + break; + case OBJTYPE_MATERIAL: + pixRowType = hiResIcons ? (m_locked ? pixMaterialDisabled2x : pixMaterialNormal2x) + : (m_locked ? pixMaterialDisabled : pixMaterialNormal); + break; + case OBJTYPE_IMAGE: + pixRowType = hiResIcons ? (m_locked ? pixImageDisabled2x : pixImageNormal2x) + : (m_locked ? pixImageDisabled : pixImageNormal); + break; + case OBJTYPE_BEHAVIOR: + pixRowType = hiResIcons ? (m_locked ? pixBehaviorDisabled2x : pixBehaviorNormal2x) + : (m_locked ? pixBehaviorDisabled : pixBehaviorNormal); + break; + case OBJTYPE_EFFECT: + pixRowType = hiResIcons ? (m_locked ? pixEffectDisabled2x : pixEffectNormal2x) + : (m_locked ? pixEffectDisabled : pixEffectNormal); + break; + default: + break; + } } - if (m_isProperty) - pixRowType = m_locked ? pixPropertyDisabled : pixPropertyNormal; - painter->drawPixmap(m_rectType, pixRowType); } +void RowTree::updateVariants(const QStringList &groups) +{ + m_variantsGroups = groups; + update(); +} + int RowTree::treeWidth() const { return m_scene->treeWidth() - m_scene->getScrollbarOffsets().x(); @@ -415,6 +524,12 @@ void RowTree::setBinding(ITimelineItemBinding *binding) updateFromBinding(); } +// x value where label should clip +int RowTree::clipX() const +{ + return treeWidth() - TimelineConstants::TREE_ICONS_W - m_variantsGroups.size() * 9 - 2; +} + ITimelineItemProperty *RowTree::propBinding() { return m_PropBinding; @@ -793,7 +908,8 @@ TreeControlType RowTree::getClickedControl(const QPointF &scenePos) if (m_rectShy.contains(p.x(), p.y())) { toggleShy(); return TreeControlType::Shy; - } else if (m_rectVisible.contains(p.x(), p.y())) { + } else if (!m_onMasterSlide && m_rectVisible.contains(p.x(), p.y())) { + // Prevent toggling hide on master slide toggleVisible(); return TreeControlType::Hide; } else if (m_rectLocked.contains(p.x(), p.y())) { diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.h index bde2da48..4524f345 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTree.h @@ -140,6 +140,7 @@ public: void updateLabel(); void setRowVisible(bool visible); void setDnDHover(bool val); + void updateVariants(const QStringList &groups); DnDState getDnDState() const; ITimelineItemBinding *getBinding() const; @@ -148,6 +149,8 @@ public: void updateFilter(); void updateLock(bool state); void updateSubpresentations(int updateParentsOnlyVal = 0); + int clipX() const; + protected: void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; @@ -181,6 +184,7 @@ private: bool m_arrowVisible = false; bool m_dndHover = false; bool m_visibilityCtrld = false; + bool m_onMasterSlide = false; DnDState m_dndState = DnDState::None; ActionStates m_actionStates = ActionState::None; bool m_hasSubpresentation = false; @@ -193,6 +197,7 @@ private: QString m_label; QList<RowTree *> m_childRows; QList<RowTree *> m_childProps; + QStringList m_variantsGroups; ITimelineItemBinding *m_binding = nullptr; ITimelineItemProperty *m_PropBinding = nullptr; // for property rows diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTreeLabelItem.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTreeLabelItem.cpp index cfd0ddd8..1029c00a 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTreeLabelItem.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/RowTreeLabelItem.cpp @@ -146,7 +146,7 @@ QRectF RowTreeLabelItem::boundingRect() const if (!m_rowTree) return QGraphicsTextItem::boundingRect(); - double w = m_rowTree->treeWidth() - x() - TimelineConstants::TREE_ICONS_W; + double w = m_rowTree->clipX() - x(); // Bounding rect width must be at least 1 w = std::max(w, 1.0); return QRectF(0, 0, w, TimelineConstants::ROW_H); diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.cpp index 86a250f4..62c6de01 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.cpp @@ -32,7 +32,6 @@ #include <QtGui/qpainter.h> #include <QtWidgets/qwidget.h> -#include <QtCore/qdebug.h> Ruler::Ruler(TimelineItem *parent) : TimelineItem(parent) { @@ -45,9 +44,9 @@ void Ruler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi double xStep = TimelineConstants::RULER_SEC_W / TimelineConstants::RULER_SEC_DIV * m_timeScale; double activeSegmentsWidth = TimelineConstants::RULER_EDGE_OFFSET - + m_duration * xStep * TimelineConstants::RULER_SEC_DIV; + + m_duration / 1000.0 * xStep * TimelineConstants::RULER_SEC_DIV; double totalSegmentsWidth = TimelineConstants::RULER_EDGE_OFFSET - + m_maxDuration * xStep * TimelineConstants::RULER_SEC_DIV; + + m_maxDuration / 1000.0 * xStep * TimelineConstants::RULER_SEC_DIV; // Ruler painted width to be at least widget width double minRulerWidth = widget->width(); @@ -80,9 +79,9 @@ void Ruler::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi if (rowX < (m_viewportX - margin) || rowX > (m_viewportX + minRulerWidth + margin)) continue; - const int h = i % secDiv == 0 ? TimelineConstants::RULER_DIV_H1 : - i % secDiv == secDiv * 0.5 ? TimelineConstants::RULER_DIV_H2 : - TimelineConstants::RULER_DIV_H3; + const int h = i % secDiv == 0 ? TimelineConstants::RULER_DIV_H1 + : i % secDiv == secDiv * 0.5 ? TimelineConstants::RULER_DIV_H2 + : TimelineConstants::RULER_DIV_H3; if (!useDisabledColor && rowX > activeSegmentsWidth) { painter->setPen(CStudioPreferences::timelineRulerColorDisabled()); @@ -122,28 +121,16 @@ void Ruler::setTimelineScale(double scl) update(); } -// convert distance values to time (seconds) -double Ruler::distanceToTime(double distance) const +// convert distance values to time (milliseconds) +long Ruler::distanceToTime(double distance) const { - return distance / (TimelineConstants::RULER_SEC_W * m_timeScale); + return distance / (TimelineConstants::RULER_MILLI_W * m_timeScale); } -// convert time (seconds) values to distance -double Ruler::timeToDistance(double time) const +// convert time (milliseconds) values to distance +double Ruler::timeToDistance(long time) const { - return time * TimelineConstants::RULER_SEC_W * m_timeScale; -} - -// x position of ruler value 0 -double Ruler::durationStartX() const -{ - return x() + TimelineConstants::RULER_EDGE_OFFSET; -} - -// x position of ruler max value (duration) -double Ruler::durationEndX() const -{ - return durationStartX() + timeToDistance(m_duration); + return time * TimelineConstants::RULER_MILLI_W * m_timeScale; } double Ruler::timelineScale() const @@ -154,7 +141,7 @@ double Ruler::timelineScale() const // Returns end of right-most layer/component row. // Active color of ruler is used up to this point. // Slide plays up to this point. -double Ruler::duration() const +long Ruler::duration() const { return m_duration; } @@ -162,12 +149,12 @@ double Ruler::duration() const // Returns end of right-most row. // Ruler steps & labels are drawn up to this point. // Timeline scrollbar allows scrolling up to this point. -double Ruler::maxDuration() const +long Ruler::maxDuration() const { return m_maxDuration; } -void Ruler::setDuration(double duration) +void Ruler::setDuration(long duration) { if (m_duration != duration) { m_duration = duration; @@ -176,7 +163,7 @@ void Ruler::setDuration(double duration) } } -void Ruler::setMaxDuration(double maxDuration) +void Ruler::setMaxDuration(long maxDuration) { if (m_maxDuration != maxDuration) { m_maxDuration = maxDuration; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.h index a234b72b..005a23c3 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/Ruler.h @@ -42,33 +42,31 @@ public: explicit Ruler(TimelineItem *parent = nullptr); void setTimelineScale(double scl); - double distanceToTime(double distance) const; - double timeToDistance(double time) const; - double durationStartX() const; - double durationEndX() const; + long distanceToTime(double distance) const; + double timeToDistance(long time) const; double timelineScale() const; - double duration() const; - double maxDuration() const; - void setDuration(double duration); - void setMaxDuration(double maxDuration); + long duration() const; + long maxDuration() const; + void setDuration(long duration); + void setMaxDuration(long maxDuration); void setViewportX(int viewportX); int viewportX() const; - int type() const; + int type() const override; protected: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; signals: - void maxDurationChanged(double maxDuration); - void durationChanged(double duration); + void maxDurationChanged(long maxDuration); + void durationChanged(long duration); void viewportXChanged(int viewportX); private: const QString timestampString(int timeMs); double m_timeScale = 2; - double m_duration = 0; // in seconds - double m_maxDuration = 0; // in seconds + long m_duration = 0; // milliseconds + long m_maxDuration = 0; // milliseconds int m_viewportX = 0; }; diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TimelineItem.h b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TimelineItem.h index 76af75ef..71d26c0d 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TimelineItem.h +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TimelineItem.h @@ -51,7 +51,7 @@ public: TypeRowMover }; - int type() const; + int type() const override; }; #endif // TIMELINEITEM_H diff --git a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TreeHeader.cpp b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TreeHeader.cpp index 295a5273..f3232b9d 100644 --- a/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TreeHeader.cpp +++ b/src/Authoring/Studio/Palettes/TimelineGraphicsView/ui/TreeHeader.cpp @@ -28,6 +28,7 @@ #include "TreeHeader.h" #include "StudioPreferences.h" +#include "StudioUtils.h" #include <QtGui/qpainter.h> @@ -45,14 +46,19 @@ void TreeHeader::paint(QPainter *painter, const QStyleOptionGraphicsItem *option Q_UNUSED(option) Q_UNUSED(widget) + bool hiResIcons = StudioUtils::devicePixelRatio(widget->window()->windowHandle()) > 1.0; + double treeWidth = m_scene->treeWidth() - m_scene->getScrollbarOffsets().x(); m_rectShy .setRect(treeWidth - 16 * 3.3, size().height() * .5 - 8, 16, 16); m_rectVisible.setRect(treeWidth - 16 * 2.2, size().height() * .5 - 8, 16, 16); m_rectLock .setRect(treeWidth - 16 * 1.1, size().height() * .5 - 8, 16, 16); - static const QPixmap pixShy = QPixmap(":/images/Toggle-Shy.png"); + static const QPixmap pixShy = QPixmap(":/images/Toggle-Shy.png"); static const QPixmap pixVisible = QPixmap(":/images/Toggle-HideShow.png"); - static const QPixmap pixLock = QPixmap(":/images/Toggle-Lock.png"); + static const QPixmap pixLock = QPixmap(":/images/Toggle-Lock.png"); + static const QPixmap pixShy2x = QPixmap(":/images/Toggle-Shy@2x.png"); + static const QPixmap pixVisible2x = QPixmap(":/images/Toggle-HideShow@2x.png"); + static const QPixmap pixLock2x = QPixmap(":/images/Toggle-Lock@2x.png"); const QColor selectedColor = CStudioPreferences::timelineFilterButtonSelectedColor(); const QColor hoveredColor = CStudioPreferences::timelineFilterButtonHoveredColor(); @@ -74,9 +80,9 @@ void TreeHeader::paint(QPainter *painter, const QStyleOptionGraphicsItem *option else if (m_hoveredItem == 2) painter->fillRect(m_rectLock, hoveredColor); - painter->drawPixmap(m_rectShy , pixShy); - painter->drawPixmap(m_rectVisible, pixVisible); - painter->drawPixmap(m_rectLock , pixLock); + painter->drawPixmap(m_rectShy , hiResIcons ? pixShy2x : pixShy); + painter->drawPixmap(m_rectVisible, hiResIcons ? pixVisible2x : pixVisible); + painter->drawPixmap(m_rectLock , hiResIcons ? pixLock2x : pixLock); } TreeControlType TreeHeader::handleButtonsClick(const QPointF &scenePos) diff --git a/src/Authoring/Studio/Qt3DStudio.pro b/src/Authoring/Studio/Qt3DStudio.pro index 28704bd3..16f850c4 100644 --- a/src/Authoring/Studio/Qt3DStudio.pro +++ b/src/Authoring/Studio/Qt3DStudio.pro @@ -201,7 +201,6 @@ HEADERS += \ Palettes/Timeline/Bindings/ITimelineItem.h \ Palettes/Timeline/Bindings/ITimelineItemBinding.h \ Palettes/Timeline/Bindings/ITimelineItemProperty.h \ - Palettes/Timeline/Bindings/ITimelineKeyframesManager.h \ Palettes/Timeline/Bindings/ITimelineTimebar.h \ Palettes/Timeline/Bindings/IBreadCrumbProvider.h \ Palettes/TimelineGraphicsView/Keyframe.h \ @@ -261,15 +260,18 @@ HEADERS += \ UI/Q3DSPlayerWidget.h \ Render/StudioEnums.h \ Render/Q3DSTranslators.h \ - Palettes/Inspector/MaterialRefView.h \ + Render/Q3DSSelectionWidget.h \ + Render/Q3DSVisualAidWidget.h \ + Render/Q3DSWidgetUtils.h \ + Render/Q3DSManipulationWidget.h \ Palettes/scenecamera/scenecameraview.h \ Palettes/scenecamera/scenecamerascrollarea.h \ Palettes/scenecamera/scenecameraglwidget.h \ + Palettes/Inspector/MaterialRefView.h \ Palettes/TimelineGraphicsView/ui/RowTimelineCommentItem.h \ - Render/Q3DSSelectionWidget.h \ - Render/Q3DSVisualAidWidget.h \ - Render/Q3DSWidgetUtils.h \ - Render/Q3DSManipulationWidget.h + Palettes/Inspector/VariantsGroupModel.h \ + Palettes/Inspector/VariantsTagModel.h \ + Palettes/Inspector/VariantTagDialog.h FORMS += \ MainFrm.ui \ @@ -289,7 +291,8 @@ FORMS += \ UI/StartupDlg.ui \ Palettes/Project/EditPresentationIdDlg.ui \ Palettes/Project/ChooseImagePropertyDlg.ui \ - Palettes/scenecamera/scenecameraview.ui + Palettes/scenecamera/scenecameraview.ui \ + Palettes/Inspector/VariantTagDialog.ui SOURCES += \ Application/AboutDlg.cpp \ @@ -434,15 +437,18 @@ SOURCES += \ UI/Q3DSPlayerWidget.cpp \ Render/Q3DSEditCamera.cpp \ Render/Q3DSTranslators.cpp \ + Render/Q3DSSelectionWidget.cpp \ + Render/Q3DSVisualAidWidget.cpp \ + Render/Q3DSWidgetUtils.cpp \ + Render/Q3DSManipulationWidget.cpp \ Palettes/Inspector/MaterialRefView.cpp \ Palettes/scenecamera/scenecameraview.cpp \ Palettes/scenecamera/scenecamerascrollarea.cpp \ Palettes/scenecamera/scenecameraglwidget.cpp \ Palettes/TimelineGraphicsView/ui/RowTimelineCommentItem.cpp \ - Render/Q3DSSelectionWidget.cpp \ - Render/Q3DSVisualAidWidget.cpp \ - Render/Q3DSWidgetUtils.cpp \ - Render/Q3DSManipulationWidget.cpp + Palettes/Inspector/VariantsGroupModel.cpp \ + Palettes/Inspector/VariantsTagModel.cpp \ + Palettes/Inspector/VariantTagDialog.cpp RESOURCES += \ MainFrm.qrc \ diff --git a/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp b/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp index c20b6727..5e8f0e96 100644 --- a/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp +++ b/src/Authoring/Studio/UI/StudioAppPrefsPage.cpp @@ -43,7 +43,6 @@ #include "Core.h" #include "IStudioRenderer.h" -#include <QtWidgets/qcolordialog.h> #include <QtWidgets/qmessagebox.h> #include <QtGui/qstandarditemmodel.h> #include <QtCore/qdiriterator.h> diff --git a/src/Authoring/Studio/Utils/StudioUtils.cpp b/src/Authoring/Studio/Utils/StudioUtils.cpp index c0063ebe..43126de1 100644 --- a/src/Authoring/Studio/Utils/StudioUtils.cpp +++ b/src/Authoring/Studio/Utils/StudioUtils.cpp @@ -70,12 +70,16 @@ qreal StudioUtils::devicePixelRatio(QWindow *window) { qreal pixelRatio = 1.0; - QWindow *w = window ? window : g_StudioApp.m_pMainWnd->windowHandle(); + QWindow *w = window ? window + : g_StudioApp.m_pMainWnd + ? g_StudioApp.m_pMainWnd->windowHandle() : nullptr; + if (w) { QScreen *s = w->screen(); if (s) pixelRatio = s->devicePixelRatio(); } + return pixelRatio; } @@ -83,7 +87,8 @@ qreal StudioUtils::devicePixelRatio(QWindow *window) bool StudioUtils::readFileToDomDocument(const QString &filePath, QDomDocument &domDoc) { QFile file(filePath); - if (!file.open(QFile::Text | QIODevice::ReadOnly)) { + if (!file.open(QIODevice::ReadOnly)) { + file.setTextModeEnabled(false); qWarning() << __FUNCTION__ << file.errorString() << "'" << filePath << "'"; return false; } @@ -96,7 +101,8 @@ bool StudioUtils::openDomDocumentSave(QSaveFile &file, QDomDocument &domDoc) { if (!readFileToDomDocument(file.fileName(), domDoc)) return false; - if (!file.open(QFile::Text | QIODevice::WriteOnly)) { + if (!file.open(QIODevice::WriteOnly)) { + file.setTextModeEnabled(false); qWarning() << __FUNCTION__ << file.errorString(); return false; } @@ -106,6 +112,8 @@ bool StudioUtils::openDomDocumentSave(QSaveFile &file, QDomDocument &domDoc) // Saves contents of a QDomDocument into a previously opened text file bool StudioUtils::commitDomDocumentSave(QSaveFile &file, const QDomDocument &domDoc) { + // Disable end-of-line conversions + file.setTextModeEnabled(false); // Overwrites entire file if (file.resize(0) && file.write(domDoc.toByteArray(4)) != -1 && file.commit()) return true; @@ -117,7 +125,8 @@ bool StudioUtils::commitDomDocumentSave(QSaveFile &file, const QDomDocument &dom // Opens text file for saving without reading its contents bool StudioUtils::openTextSave(QSaveFile &file) { - if (!file.open(QFile::Text | QIODevice::WriteOnly)) { + if (!file.open(QIODevice::WriteOnly)) { + file.setTextModeEnabled(false); qWarning() << __FUNCTION__ << file.errorString(); return false; } diff --git a/src/Authoring/Studio/Utils/StudioUtils.h b/src/Authoring/Studio/Utils/StudioUtils.h index 945ffeb2..f1db4c57 100644 --- a/src/Authoring/Studio/Utils/StudioUtils.h +++ b/src/Authoring/Studio/Utils/StudioUtils.h @@ -33,6 +33,7 @@ #include <QtXml/qdom.h> #include <QtCore/qsavefile.h> #include <QtCore/qxmlstream.h> +#include <QtGui/qwindow.h> QT_FORWARD_DECLARE_CLASS(QWindow) diff --git a/src/Authoring/Studio/Workspace/Dialogs.cpp b/src/Authoring/Studio/Workspace/Dialogs.cpp index c32d1568..a6e9f605 100644 --- a/src/Authoring/Studio/Workspace/Dialogs.cpp +++ b/src/Authoring/Studio/Workspace/Dialogs.cpp @@ -1037,6 +1037,42 @@ QString CDialogs::GetSaveAsChoice(const QString &inDialogTitle, bool isProject, return theFile; } +QString CDialogs::getImportVariantsDlg() +{ + QString docDir = QFileInfo(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath()).absolutePath(); + + QFileDialog dlg; + dlg.setDirectory(docDir); + dlg.setWindowTitle(tr("Import variants")); + dlg.setDefaultSuffix(QStringLiteral(".variants")); + dlg.setNameFilters({tr("All supported files (*.variants *.uia)"), + tr("Variants files (*.variants)"), tr("Project files (*.uia)")}); + dlg.exec(); + + if (!dlg.selectedFiles().empty()) + return dlg.selectedFiles().front(); + + return {}; +} + +QString CDialogs::getExportVariantsDlg() +{ + QString docDir = QFileInfo(g_StudioApp.GetCore()->GetDoc()->GetDocumentPath()).absolutePath(); + + QFileDialog dlg; + dlg.setDirectory(docDir); + dlg.setAcceptMode(QFileDialog::AcceptSave); + dlg.setWindowTitle(tr("Export variants")); + dlg.setDefaultSuffix(QStringLiteral(".variants")); + dlg.setNameFilters({QObject::tr("Variants files (*.variants)")}); + dlg.exec(); + + if (!dlg.selectedFiles().empty()) + return dlg.selectedFiles().front(); + + return {}; +} + //============================================================================== /** * Prompt the user for a file to create. @@ -1225,21 +1261,20 @@ void CDialogs::DisplayGLVersionWarning(const Q3DStudio::CString &inGLVersion, } void CDialogs::asyncDisplayTimeEditDialog(long time, IDoc *doc, long objectAssociation, - ITimelineKeyframesManager *keyframesManager) const + KeyframeManager *keyframesManager) const { QTimer::singleShot(0, [time, doc, objectAssociation, keyframesManager]() { - CTimeEditDlg timeEditDlg; - timeEditDlg.setKeyframesManager(keyframesManager); + CTimeEditDlg timeEditDlg(keyframesManager); timeEditDlg.showDialog(time, doc, objectAssociation); }); } -void CDialogs::asyncDisplayDurationEditDialog(long startTime, long endTime, IDoc *doc, +void CDialogs::asyncDisplayDurationEditDialog(long startTime, long endTime, ITimeChangeCallback *callback) const { - QTimer::singleShot(0, [startTime, endTime, doc, callback]() { + QTimer::singleShot(0, [startTime, endTime, callback]() { CDurationEditDlg durationEditDlg; - durationEditDlg.showDialog(startTime, endTime, doc, callback); + durationEditDlg.showDialog(startTime, endTime, callback); }); } @@ -1380,8 +1415,8 @@ QStringList CDialogs::qmlStreamExtensions() QColor CDialogs::displayColorDialog(const QColor &color) const { QColorDialog theColorDlg; - theColorDlg.setCurrentColor(color); theColorDlg.setOption(QColorDialog::DontUseNativeDialog, true); + theColorDlg.setCurrentColor(color); connect(&theColorDlg, &QColorDialog::currentColorChanged, this, &CDialogs::onColorChanged); int result = theColorDlg.exec(); disconnect(&theColorDlg, &QColorDialog::currentColorChanged, this, &CDialogs::onColorChanged); diff --git a/src/Authoring/Studio/Workspace/Dialogs.h b/src/Authoring/Studio/Workspace/Dialogs.h index d8163eb8..c54f17a1 100644 --- a/src/Authoring/Studio/Workspace/Dialogs.h +++ b/src/Authoring/Studio/Workspace/Dialogs.h @@ -55,7 +55,7 @@ class CStudioApp; class CControl; class CDialogControl; class CProgressView; -class ITimelineKeyframesManager; +class KeyframeManager; class ITimeChangeCallback; class CDialogs : public QObject @@ -120,6 +120,8 @@ public: bool isCopy = false); QString GetNewDocumentChoice(const QString &inInitialDirectory = {}, bool isProject = true); QString GetFileOpenChoice(const QString &inInitialDirectory = {}); + QString getExportVariantsDlg(); + QString getImportVariantsDlg(); void DisplayImportFailed(const QUrl &inURL, const QString &inDescription, bool inWarningsOnly); void DisplayLoadingPresentationFailed(const QFileInfo &loadFileInfo, @@ -166,8 +168,8 @@ public: const Q3DStudio::CString &inRecommendedVersion); void asyncDisplayTimeEditDialog(long time, IDoc *doc, long objectAssociation, - ITimelineKeyframesManager *keyframesManager = nullptr) const; - void asyncDisplayDurationEditDialog(long startTime, long endTime, IDoc *doc, + KeyframeManager *keyframesManager = nullptr) const; + void asyncDisplayDurationEditDialog(long startTime, long endTime, ITimeChangeCallback *callback) const; enum class WidgetBrowserAlign { diff --git a/src/Authoring/Studio/images.qrc b/src/Authoring/Studio/images.qrc index e57eee1d..0c6e16f5 100644 --- a/src/Authoring/Studio/images.qrc +++ b/src/Authoring/Studio/images.qrc @@ -286,6 +286,29 @@ <file>images/matdef-active@2x.png</file> <file>images/matdef-disabled.png</file> <file>images/matdef-disabled@2x.png</file> + <file>images/playback_tools_low-04@2x.png</file> + <file>images/Action-Action@2x.png</file> + <file>images/Action-ChildAction@2x.png</file> + <file>images/Action-ChildMasterAction@2x.png</file> + <file>images/Action-ComponentAction@2x.png</file> + <file>images/Action-ComponentMasterAction@2x.png</file> + <file>images/Action-MasterAction@2x.png</file> + <file>images/Insert-Left@2x.png</file> + <file>images/Insert-Right@2x.png</file> + <file>images/Toggle-Empty@2x.png</file> + <file>images/keyframe-hidden-normal@2x.png</file> + <file>images/Keyframe-MasterDynamic-Selected@2x.png</file> + <file>images/Keyframe-MasterDynamic-Normal@2x.png</file> + <file>images/Keyframe-MasterDynamic-Disabled@2x.png</file> + <file>images/Keyframe-Master-Selected@2x.png</file> + <file>images/Keyframe-Master-Normal@2x.png</file> + <file>images/Keyframe-Master-Disabled@2x.png</file> + <file>images/Keyframe-PropertyDynamic-Selected@2x.png</file> + <file>images/Keyframe-PropertyDynamic-Normal@2x.png</file> + <file>images/Keyframe-PropertyDynamic-Disabled@2x.png</file> + <file>images/Keyframe-Property-Selected@2x.png</file> + <file>images/Keyframe-Property-Normal@2x.png</file> + <file>images/Keyframe-Property-Disabled@2x.png</file> </qresource> <qresource prefix="/startup"> <file alias="open_dialog.png">images/open_dialog.png</file> diff --git a/src/Authoring/Studio/images/Action-Action@2x.png b/src/Authoring/Studio/images/Action-Action@2x.png Binary files differnew file mode 100644 index 00000000..affa6c01 --- /dev/null +++ b/src/Authoring/Studio/images/Action-Action@2x.png diff --git a/src/Authoring/Studio/images/Action-ChildAction@2x.png b/src/Authoring/Studio/images/Action-ChildAction@2x.png Binary files differnew file mode 100644 index 00000000..ba066860 --- /dev/null +++ b/src/Authoring/Studio/images/Action-ChildAction@2x.png diff --git a/src/Authoring/Studio/images/Action-ChildMasterAction@2x.png b/src/Authoring/Studio/images/Action-ChildMasterAction@2x.png Binary files differnew file mode 100644 index 00000000..f0c8a1da --- /dev/null +++ b/src/Authoring/Studio/images/Action-ChildMasterAction@2x.png diff --git a/src/Authoring/Studio/images/Action-ComponentAction@2x.png b/src/Authoring/Studio/images/Action-ComponentAction@2x.png Binary files differnew file mode 100644 index 00000000..26a2c36c --- /dev/null +++ b/src/Authoring/Studio/images/Action-ComponentAction@2x.png diff --git a/src/Authoring/Studio/images/Action-ComponentMasterAction@2x.png b/src/Authoring/Studio/images/Action-ComponentMasterAction@2x.png Binary files differnew file mode 100644 index 00000000..fa40fb4b --- /dev/null +++ b/src/Authoring/Studio/images/Action-ComponentMasterAction@2x.png diff --git a/src/Authoring/Studio/images/Action-MasterAction@2x.png b/src/Authoring/Studio/images/Action-MasterAction@2x.png Binary files differnew file mode 100644 index 00000000..c3b8e2e5 --- /dev/null +++ b/src/Authoring/Studio/images/Action-MasterAction@2x.png diff --git a/src/Authoring/Studio/images/Insert-Left@2x.png b/src/Authoring/Studio/images/Insert-Left@2x.png Binary files differnew file mode 100644 index 00000000..ad0696c6 --- /dev/null +++ b/src/Authoring/Studio/images/Insert-Left@2x.png diff --git a/src/Authoring/Studio/images/Insert-Right@2x.png b/src/Authoring/Studio/images/Insert-Right@2x.png Binary files differnew file mode 100644 index 00000000..d7a20850 --- /dev/null +++ b/src/Authoring/Studio/images/Insert-Right@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Disabled.png b/src/Authoring/Studio/images/Keyframe-Master-Disabled.png Binary files differindex 7e66cd27..41c7bbe3 100644 --- a/src/Authoring/Studio/images/Keyframe-Master-Disabled.png +++ b/src/Authoring/Studio/images/Keyframe-Master-Disabled.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Disabled@2x.png b/src/Authoring/Studio/images/Keyframe-Master-Disabled@2x.png Binary files differnew file mode 100644 index 00000000..a1291e92 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Master-Disabled@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Normal.png b/src/Authoring/Studio/images/Keyframe-Master-Normal.png Binary files differindex 9fb6281b..af88c894 100644 --- a/src/Authoring/Studio/images/Keyframe-Master-Normal.png +++ b/src/Authoring/Studio/images/Keyframe-Master-Normal.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Normal@2x.png b/src/Authoring/Studio/images/Keyframe-Master-Normal@2x.png Binary files differnew file mode 100644 index 00000000..503942c8 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Master-Normal@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Selected.png b/src/Authoring/Studio/images/Keyframe-Master-Selected.png Binary files differindex 5d3806ba..7e27743c 100644 --- a/src/Authoring/Studio/images/Keyframe-Master-Selected.png +++ b/src/Authoring/Studio/images/Keyframe-Master-Selected.png diff --git a/src/Authoring/Studio/images/Keyframe-Master-Selected@2x.png b/src/Authoring/Studio/images/Keyframe-Master-Selected@2x.png Binary files differnew file mode 100644 index 00000000..e86bacf5 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Master-Selected@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled.png Binary files differindex 734f8e2c..d6abe9f1 100644 --- a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled.png +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled@2x.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled@2x.png Binary files differnew file mode 100644 index 00000000..0b90181d --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Disabled@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png Binary files differindex 919c310f..6d6dab76 100644 --- a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal@2x.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal@2x.png Binary files differnew file mode 100644 index 00000000..4a1f83fc --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Normal@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png Binary files differindex 7712d96f..6e00aeba 100644 --- a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected.png diff --git a/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected@2x.png b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected@2x.png Binary files differnew file mode 100644 index 00000000..c402defc --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-MasterDynamic-Selected@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Disabled.png b/src/Authoring/Studio/images/Keyframe-Property-Disabled.png Binary files differindex 87e5dde9..2bba3f4b 100644 --- a/src/Authoring/Studio/images/Keyframe-Property-Disabled.png +++ b/src/Authoring/Studio/images/Keyframe-Property-Disabled.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Disabled@2x.png b/src/Authoring/Studio/images/Keyframe-Property-Disabled@2x.png Binary files differnew file mode 100644 index 00000000..957fb36e --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Property-Disabled@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Normal.png b/src/Authoring/Studio/images/Keyframe-Property-Normal.png Binary files differindex 7afc5b09..aaebda90 100644 --- a/src/Authoring/Studio/images/Keyframe-Property-Normal.png +++ b/src/Authoring/Studio/images/Keyframe-Property-Normal.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Normal@2x.png b/src/Authoring/Studio/images/Keyframe-Property-Normal@2x.png Binary files differnew file mode 100644 index 00000000..06919fa3 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Property-Normal@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Selected.png b/src/Authoring/Studio/images/Keyframe-Property-Selected.png Binary files differindex 7eb45ab3..456a4b90 100644 --- a/src/Authoring/Studio/images/Keyframe-Property-Selected.png +++ b/src/Authoring/Studio/images/Keyframe-Property-Selected.png diff --git a/src/Authoring/Studio/images/Keyframe-Property-Selected@2x.png b/src/Authoring/Studio/images/Keyframe-Property-Selected@2x.png Binary files differnew file mode 100644 index 00000000..b2008a29 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-Property-Selected@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled.png Binary files differindex 8f23ea26..84263b0d 100644 --- a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled.png +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled@2x.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled@2x.png Binary files differnew file mode 100644 index 00000000..229fddf2 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Disabled@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png Binary files differindex 2231a10c..733e0718 100644 --- a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png Binary files differnew file mode 100644 index 00000000..aa24ccac --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png~ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png~ Binary files differnew file mode 100644 index 00000000..aa24ccac --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Normal@2x.png~ diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png Binary files differindex 02186956..ef1cf1bd 100644 --- a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected.png diff --git a/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected@2x.png b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected@2x.png Binary files differnew file mode 100644 index 00000000..ad155725 --- /dev/null +++ b/src/Authoring/Studio/images/Keyframe-PropertyDynamic-Selected@2x.png diff --git a/src/Authoring/Studio/images/Toggle-Empty@2x.png b/src/Authoring/Studio/images/Toggle-Empty@2x.png Binary files differnew file mode 100644 index 00000000..94c695c6 --- /dev/null +++ b/src/Authoring/Studio/images/Toggle-Empty@2x.png diff --git a/src/Authoring/Studio/images/keyframe-hidden-normal@2x.png b/src/Authoring/Studio/images/keyframe-hidden-normal@2x.png Binary files differnew file mode 100644 index 00000000..040d281d --- /dev/null +++ b/src/Authoring/Studio/images/keyframe-hidden-normal@2x.png diff --git a/src/Authoring/Studio/images/playback_tools_low-04@2x.png b/src/Authoring/Studio/images/playback_tools_low-04@2x.png Binary files differnew file mode 100644 index 00000000..0f74d37e --- /dev/null +++ b/src/Authoring/Studio/images/playback_tools_low-04@2x.png diff --git a/src/Runtime/qt3d-runtime b/src/Runtime/qt3d-runtime -Subproject 34e028b916fb1d215cdbb20fe1fecb677145cb5 +Subproject 72c4b5fc9d7fde2a2bdf730eab22ffb99b9c2ca diff --git a/src/shared/header/foundation/ConvertUTF.h b/src/shared/header/foundation/ConvertUTF.h index 14b88c7c..e5a917bd 100644 --- a/src/shared/header/foundation/ConvertUTF.h +++ b/src/shared/header/foundation/ConvertUTF.h @@ -146,25 +146,33 @@ typedef enum { strictConversion = 0, lenientConversion } ConversionFlags; extern "C" { #endif -ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, - UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); +// Note: Functions renamed from original Unicode functions to avoid conflicting with the same +// functions in breakpad implementation which is also statically linked +ConversionResult Unicode_ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags); -ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, - UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); +ConversionResult Unicode_ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, + ConversionFlags flags); -ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, - UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); +ConversionResult Unicode_ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags); -ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, - UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); +ConversionResult Unicode_ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, + ConversionFlags flags); -ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, - UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); +ConversionResult Unicode_ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, + ConversionFlags flags); -ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, - UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); +ConversionResult Unicode_ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, + ConversionFlags flags); -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); +Boolean Unicode_isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); #ifdef __cplusplus } diff --git a/src/shared/header/foundation/StrConvertUTF.h b/src/shared/header/foundation/StrConvertUTF.h index 678411e2..81428fef 100644 --- a/src/shared/header/foundation/StrConvertUTF.h +++ b/src/shared/header/foundation/StrConvertUTF.h @@ -91,7 +91,7 @@ namespace foundation { UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { - return ConvertUTF8toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF8toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; @@ -102,7 +102,7 @@ namespace foundation { UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { - return ConvertUTF8toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF8toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; template <> @@ -111,7 +111,7 @@ namespace foundation { static ConversionResult Convert(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { - return ConvertUTF16toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF16toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; template <> @@ -121,7 +121,7 @@ namespace foundation { UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { - return ConvertUTF16toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF16toUTF32(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; template <> @@ -130,7 +130,7 @@ namespace foundation { static ConversionResult Convert(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { - return ConvertUTF32toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF32toUTF8(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; template <> @@ -140,7 +140,7 @@ namespace foundation { UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { - return ConvertUTF32toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); + return Unicode_ConvertUTF32toUTF16(sourceStart, sourceEnd, targetStart, targetEnd, flags); } }; diff --git a/src/shared/qt-breakpad/qtcrashhandler/mainwidget.h b/src/shared/qt-breakpad/qtcrashhandler/mainwidget.h index 4cb00a38..bd727997 100644 --- a/src/shared/qt-breakpad/qtcrashhandler/mainwidget.h +++ b/src/shared/qt-breakpad/qtcrashhandler/mainwidget.h @@ -34,7 +34,13 @@ #include <QtWidgets/qwidget.h> #include <QtCore/qpointer.h> +#ifdef QT_NAMESPACE +using namespace QT_NAMESPACE; +#endif + +QT_BEGIN_NAMESPACE namespace Ui { class MainWidget; } +QT_END_NAMESPACE class MainWidget : public QWidget { diff --git a/src/shared/source/ConvertUTF.cpp b/src/shared/source/ConvertUTF.cpp index 14017948..d00040e6 100644 --- a/src/shared/source/ConvertUTF.cpp +++ b/src/shared/source/ConvertUTF.cpp @@ -89,7 +89,7 @@ static const UTF32 halfMask = 0x3FFUL; /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, +ConversionResult Unicode_ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; @@ -141,7 +141,7 @@ ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sou /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, +ConversionResult Unicode_ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; @@ -247,7 +247,7 @@ static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, +ConversionResult Unicode_ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; @@ -399,7 +399,7 @@ static Boolean isLegalUTF8(const UTF8 *source, int length) * Exported function to return whether a UTF-8 sequence is legal or not. * This is not used here; it's just exported. */ -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) +Boolean Unicode_isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { int length = trailingBytesForUTF8[*source] + 1; if (source + length > sourceEnd) { @@ -410,7 +410,7 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, +ConversionResult Unicode_ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; @@ -497,7 +497,7 @@ ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *source /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, +ConversionResult Unicode_ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; @@ -564,7 +564,7 @@ ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sour /* --------------------------------------------------------------------- */ -ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, +ConversionResult Unicode_ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { ConversionResult result = conversionOK; |