diff options
Diffstat (limited to 'src/Authoring/Studio/Application')
-rw-r--r-- | src/Authoring/Studio/Application/DataInputDlg.cpp | 34 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DataInputDlg.h | 4 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DataInputDlg.ui | 42 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DataInputListDlg.cpp | 41 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DurationEditDlg.cpp | 214 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DurationEditDlg.h | 35 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/DurationEditDlg.ui | 59 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/ProjectFile.cpp | 677 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/ProjectFile.h | 25 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/StudioApp.cpp | 13 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/TimeEditDlg.cpp | 210 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/TimeEditDlg.h | 29 | ||||
-rw-r--r-- | src/Authoring/Studio/Application/TimeEditDlg.ui | 68 |
13 files changed, 1044 insertions, 407 deletions
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> |