Explorar o código

Added initial tag line edit field

Signed-off-by: T.J. Kotha <[email protected]>
T.J. Kotha %!s(int64=2) %!d(string=hai) anos
pai
achega
d13023d64e

+ 35 - 0
Code/Tools/ProjectManager/Resources/ProjectManager.qss

@@ -66,6 +66,10 @@ QStackedWidget#createAGemRHS{
     margin-left: 0px;
 }
 
+#createAGemRHS #formTagField{
+    margin-left: 0px;
+}
+
 QTabWidget::pane {
     background-color: #333333;
     border:0 none;
@@ -161,6 +165,30 @@ QTabBar::tab:focus {
     margin-left:50px;
 }
 
+#formTagField {
+    max-width: 840px;
+    background-color: #333333;
+    padding: 0px 10px 2px 6px;
+    margin-left:50px;
+}
+
+#formTagField QCheckBox{
+    background-color: #555555;
+    padding-left:5px;
+    padding-top:2px;
+    padding-bottom:2px;
+    padding-right:5px;
+    spacing:0px;
+    color:#94D2FF;
+}
+
+#formTagField QCheckBox::indicator{
+    background-color: transparent;
+    image:url(:/X.svg);
+    border:0px;
+    width: 10%;
+}
+
 #createAGem #formFrame {
     margin-left: 0px;
 }
@@ -192,6 +220,13 @@ QTabBar::tab:focus {
     qproperty-flat: true;
 }
 
+#formFrame #dropDownButtonFrame QPushButton {
+    background-color: transparent;
+    background:transparent url(:/CaarrotArrowDown.svg) no-repeat center;
+    qproperty-flat: true;
+}
+
+
 #formFrame QPushButton:focus {
     border:none;
 }

+ 3 - 3
Code/Tools/ProjectManager/Source/CreateAGemScreen.cpp

@@ -269,8 +269,8 @@ namespace O3DE::ProjectManager
         m_licenseURL = new FormLineEditWidget(tr("License URL"), "", tr("Link to the license web site i.e. https://opensource.org/licenses/Apache-2.0"), "");
         gemDetailsLayout->addWidget(m_licenseURL);
 
-        m_userDefinedGemTags = new FormLineEditWidget(tr("User-defined Gem Tags (Comma separated list)"), "");
-        m_userDefinedGemTags->lineEdit()->setValidator(new QRegularExpressionValidator(QRegularExpression("(\\w+)(,\\s?\\w*)*"), this));
+        m_userDefinedGemTags = new FormLineEditTagsWidget(tr("User-defined Gem Tags (Press Space or Return with non-empty text to create a new tag)"), "");
+        m_userDefinedGemTags->lineEdit()->setValidator(new QRegularExpressionValidator(QRegularExpression("(^$|((\\w+)(\\s?\\w*)*))"), this));
         gemDetailsLayout->addWidget(m_userDefinedGemTags);
 
         m_gemLocation = new FormFolderBrowseEditWidget(tr("Gem Location"), "", tr("The path that the gem will be created at."), tr("The chosen directory must either not exist or be empty."));
@@ -438,7 +438,7 @@ namespace O3DE::ProjectManager
                 m_createGemInfo.m_licenseLink = m_licenseURL->lineEdit()->text();
                 m_createGemInfo.m_documentationLink = m_documentationURL->lineEdit()->text();
                 m_createGemInfo.m_path = m_gemLocation->lineEdit()->text();
-                m_createGemInfo.m_features = m_userDefinedGemTags->lineEdit()->text().split(',');
+                m_createGemInfo.m_features = m_userDefinedGemTags->getTags();
 
                 m_stackWidget->setCurrentIndex(GemCreatorDetailsScreen);
                 m_nextButton->setText(tr("Create"));

+ 2 - 1
Code/Tools/ProjectManager/Source/CreateAGemScreen.h

@@ -12,6 +12,7 @@
 #include <ScreenWidget.h>
 #include <FormFolderBrowseEditWidget.h>
 #include <FormLineEditWidget.h>
+#include <FormLineEditTagsWidget.h>
 #include <FormComboBoxWidget.h>
 #include <GemCatalog/GemInfo.h>
 #include <PythonBindings.h>
@@ -70,7 +71,7 @@ namespace O3DE::ProjectManager
         FormLineEditWidget* m_requirements = nullptr;
         FormLineEditWidget* m_license = nullptr;
         FormLineEditWidget* m_licenseURL = nullptr;
-        FormLineEditWidget* m_userDefinedGemTags = nullptr;
+        FormLineEditTagsWidget* m_userDefinedGemTags = nullptr;
         FormFolderBrowseEditWidget* m_gemLocation = nullptr;
         FormLineEditWidget* m_gemIconPath = nullptr;
         FormLineEditWidget* m_documentationURL = nullptr;

+ 345 - 0
Code/Tools/ProjectManager/Source/FormLineEditTagsWidget.cpp

@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <FormLineEditTagsWidget.h>
+#include <AzQtComponents/Components/StyledLineEdit.h>
+#include <AzQtComponents/Components/Widgets/LineEdit.h>
+#include <QPushButton>
+#include <QAbstractItemView>
+#include <QSpacerItem>
+#include <QCompleter>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QLabel>
+#include <QMovie>
+#include <QFrame>
+#include <QValidator>
+#include <QStyle>
+#include <QKeyEvent>
+
+namespace O3DE::ProjectManager
+{
+
+    void FormLineEditTagsWidget::setupCompletionTags()
+    {
+        m_completionTags << "Audio" << "Animation" << "Physics" << "GameDevelopment" << "IK" << "Blender" << "Rendering" << "Terrain";
+    }
+
+    FormLineEditTagsWidget::FormLineEditTagsWidget(
+        const QString& labelText,
+        const QString& valueText,
+        const QString& placeholderText,
+        const QString& errorText,
+        QWidget* parent)
+        : QWidget(parent)
+        
+    {
+        setObjectName("formLineEditWidget");
+
+        setupCompletionTags();
+
+        QVBoxLayout* mainLayout = new QVBoxLayout();
+        mainLayout->setAlignment(Qt::AlignTop);
+        {
+            m_frame = new QFrame(this);
+            m_frame->setObjectName("formFrame");
+
+            // use a horizontal box layout so buttons can be added to the right of the field
+            m_frameLayout = new QHBoxLayout();
+            {
+                QVBoxLayout* fieldLayout = new QVBoxLayout();
+
+                QLabel* label = new QLabel(labelText, this);
+                fieldLayout->addWidget(label);
+
+                m_lineEdit = new AzQtComponents::StyledLineEdit(this);
+                m_lineEdit->setFlavor(AzQtComponents::StyledLineEdit::Question);
+                AzQtComponents::LineEdit::setErrorIconEnabled(m_lineEdit, false);
+                m_lineEdit->setText(valueText);
+                m_lineEdit->setPlaceholderText(placeholderText);
+
+                connect(m_lineEdit, &AzQtComponents::StyledLineEdit::flavorChanged, this, &FormLineEditTagsWidget::flavorChanged);
+                connect(m_lineEdit, &AzQtComponents::StyledLineEdit::onFocus, this, &FormLineEditTagsWidget::onFocus);
+                connect(m_lineEdit, &AzQtComponents::StyledLineEdit::onFocusOut, this, &FormLineEditTagsWidget::onFocusOut);
+                connect(m_lineEdit, &AzQtComponents::StyledLineEdit::textChanged, this, &FormLineEditTagsWidget::textChanged);
+
+                m_lineEdit->setFrame(false);
+                fieldLayout->addWidget(m_lineEdit);
+
+                m_frameLayout->addLayout(fieldLayout);
+
+                QWidget* emptyWidget = new QWidget(this);
+                m_frameLayout->addWidget(emptyWidget);
+
+                m_processingSpinnerMovie = new QMovie(":/in_progress.gif");
+                m_processingSpinner = new QLabel(this);
+                m_processingSpinner->setScaledContents(true);
+                m_processingSpinner->setMaximumSize(32, 32);
+                m_processingSpinner->setMovie(m_processingSpinnerMovie);
+                m_frameLayout->addWidget(m_processingSpinner);
+
+                m_validationErrorIcon = new QLabel(this);
+                m_validationErrorIcon->setPixmap(QIcon(":/error.svg").pixmap(32, 32));
+                m_frameLayout->addWidget(m_validationErrorIcon);
+
+                m_validationSuccessIcon = new QLabel(this);
+                m_validationSuccessIcon->setPixmap(QIcon(":/checkmark.svg").pixmap(32, 32));
+                m_frameLayout->addWidget(m_validationSuccessIcon);
+
+                QFrame* buttonFrame = new QFrame(this);
+                buttonFrame->setObjectName("dropDownButtonFrame");
+                QVBoxLayout* buttonLayout = new QVBoxLayout(this);
+                m_dropdownButton = new QPushButton(QIcon(":/CarrotArrowDown.svg"), "", this);
+                buttonLayout->addWidget(m_dropdownButton);
+                buttonFrame->setLayout(buttonLayout);
+                m_frameLayout->addWidget(buttonFrame);
+
+                SetValidationState(ValidationState::NotValidating);
+
+                m_completer = new QCompleter(m_completionTags, this);
+                m_completer->setCaseSensitivity(Qt::CaseInsensitive);
+                m_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion);
+                m_completer->popup()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+                m_completer->popup()->setFixedWidth(m_lineEdit->rect().width()*3);
+                m_lineEdit->setCompleter(m_completer);
+                //it's possible this responds best to signals from line edit?
+                connect(m_dropdownButton, &QPushButton::clicked, this, [=]([[maybe_unused]]bool ignore){ m_lineEdit->completer()->complete(QRect()); });
+                connect(m_completer, QOverload<const QString&>::of(&QCompleter::highlighted), this, &FormLineEditTagsWidget::forceSetText);
+                connect(m_completer, QOverload<const QString&>::of(&QCompleter::activated), this, &FormLineEditTagsWidget::forceSetText);
+            }
+
+            m_frame->setLayout(m_frameLayout);
+
+            mainLayout->addWidget(m_frame);
+
+            
+            m_errorLabel = new QLabel(this);
+            m_errorLabel->setObjectName("formErrorLabel");
+            m_errorLabel->setText(errorText);
+            m_errorLabel->setVisible(false);
+            mainLayout->addWidget(m_errorLabel);
+
+            m_tagFrame = new QFrame(this);
+            m_tagFrame->setObjectName("formTagField");
+            
+            QHBoxLayout* tagsLayout = new QHBoxLayout();
+            tagsLayout->setSpacing(8);
+            tagsLayout->addStretch();
+            
+            m_tagFrame->setLayout(tagsLayout);
+            m_tagFrame->setVisible(false);
+            
+
+            tagsLayout->setSizeConstraint(QLayout::SetNoConstraint);
+
+            mainLayout->addWidget(m_tagFrame);
+
+        }
+
+        setLayout(mainLayout);
+    }
+
+    FormLineEditTagsWidget::FormLineEditTagsWidget(const QString& labelText, const QString& valueText, QWidget* parent)
+        : FormLineEditTagsWidget(labelText, valueText, "", "", parent)
+    {
+    }
+
+    void FormLineEditTagsWidget::forceSetText(const QString& text)
+    {
+        m_lineEdit->setText(text);
+        m_lineEdit->setFocus();
+    }
+
+    void FormLineEditTagsWidget::forceSubmitCurrentText()
+    {
+        addToTagList(m_lineEdit->text());
+        m_lineEdit->clear();
+        m_lineEdit->setText("");
+        refreshTagFrame();
+    }
+
+    void FormLineEditTagsWidget::refreshTagFrame()
+    {
+        // cleanup the tag frame widget and re-add the tag list
+        qDeleteAll(m_tagFrame->children());
+        QHBoxLayout* layout = new QHBoxLayout(this);
+
+        for (auto t : m_tags)
+        {
+            QCheckBox* tc = new QCheckBox(t, this);
+            tc->setLayoutDirection(Qt::RightToLeft);
+            // connect the checked signal to a good slot
+            connect(tc, &QCheckBox::stateChanged, this, &FormLineEditTagsWidget::processTagDelete);
+            layout->addWidget(tc);
+        }
+        layout->addStretch();
+        layout->setSizeConstraint(QLayout::SetNoConstraint);
+        m_tagFrame->setLayout(layout);
+
+        bool makeVisible = m_tags.count() > 0;
+        m_tagFrame->setVisible(makeVisible);
+        refreshStyle();
+    }
+
+    void FormLineEditTagsWidget::addToTagList(const QString& text)
+    {
+        if((QString::compare(text,"") > 0 || QString::compare(text,"") < 0) && !m_tags.contains(text))
+        {
+            m_tags << text;
+        }
+    }
+
+    void FormLineEditTagsWidget::textChanged(const QString& text)
+    {
+        if(text.contains(" "))
+        {
+            //do tag adding
+            QString interim = m_lineEdit->text();
+            m_lineEdit->clear();
+            for(auto str : interim.split(" "))
+            {
+                addToTagList(str);
+            }
+            refreshTagFrame();
+        }
+    } 
+
+    void FormLineEditTagsWidget::processTagDelete([[maybe_unused]] int unused)
+    {
+        auto checkBoxes = m_tagFrame->findChildren<QCheckBox* >();
+        QString string;
+        bool found = false;
+        for(auto c : checkBoxes)
+        {
+            if(c->isChecked())
+            {
+                string = c->text();
+                found = true;
+                break;
+            }
+        }
+
+        if(!found)
+        {
+            return;
+        }
+        //remove the offending string
+        m_tags.removeOne(string);
+
+        //now reconstruct the tag list
+        refreshTagFrame();
+    }
+
+    void FormLineEditTagsWidget::setErrorLabelText(const QString& labelText)
+    {
+        m_errorLabel->setText(labelText);
+    }
+
+    void FormLineEditTagsWidget::setErrorLabelVisible(bool visible)
+    {
+        m_errorLabel->setVisible(visible);
+        m_frame->setProperty("Valid", !visible);
+
+        refreshStyle();
+    }
+
+    QLineEdit* FormLineEditTagsWidget::lineEdit() const
+    {
+        return m_lineEdit;
+    }
+
+    void FormLineEditTagsWidget::flavorChanged()
+    {
+        if (m_lineEdit->flavor() == AzQtComponents::StyledLineEdit::Flavor::Invalid)
+        {
+            m_frame->setProperty("Valid", false);
+            m_errorLabel->setVisible(true);
+        }
+        else
+        {
+            m_frame->setProperty("Valid", true);
+            m_errorLabel->setVisible(false);
+        }
+        refreshStyle();
+    }
+
+    void FormLineEditTagsWidget::onFocus()
+    {
+        m_frame->setProperty("Focus", true);
+        refreshStyle();
+    }
+
+    void FormLineEditTagsWidget::onFocusOut()
+    {
+        m_frame->setProperty("Focus", false);
+        refreshStyle();
+    }
+
+    void FormLineEditTagsWidget::refreshStyle()
+    {
+        // we must unpolish/polish every child after changing a property
+        // or else they won't use the correct stylesheet selector
+        for (auto child : findChildren<QWidget*>())
+        {
+            child->style()->unpolish(child);
+            child->style()->polish(child);
+        }
+    }
+
+    void FormLineEditTagsWidget::setText(const QString& text)
+    {
+        m_lineEdit->setText(text);
+    }
+
+    void FormLineEditTagsWidget::SetValidationState(ValidationState validationState)
+    {
+        switch (validationState)
+        {
+        case ValidationState::Validating:
+            m_processingSpinnerMovie->start();
+            m_processingSpinner->setVisible(true);
+            m_validationErrorIcon->setVisible(false);
+            m_validationSuccessIcon->setVisible(false);
+            break;
+        case ValidationState::ValidationSuccess:
+            m_processingSpinnerMovie->stop();
+            m_processingSpinner->setVisible(false);
+            m_validationErrorIcon->setVisible(false);
+            m_validationSuccessIcon->setVisible(true);
+            break;
+        case ValidationState::ValidationFailed:
+            m_processingSpinnerMovie->stop();
+            m_processingSpinner->setVisible(false);
+            m_validationErrorIcon->setVisible(true);
+            m_validationSuccessIcon->setVisible(false);
+            break;
+        case ValidationState::NotValidating:
+        default:
+            m_processingSpinnerMovie->stop();
+            m_processingSpinner->setVisible(false);
+            m_validationErrorIcon->setVisible(false);
+            m_validationSuccessIcon->setVisible(false);
+            break;
+        }
+    }
+
+    void FormLineEditTagsWidget::mousePressEvent([[maybe_unused]] QMouseEvent* event)
+    {
+        m_lineEdit->setFocus();
+    }
+
+    void FormLineEditTagsWidget::keyPressEvent(QKeyEvent* event)
+    {
+        if (event->key() == Qt::Key_Return)
+        {
+            forceSubmitCurrentText();
+        }
+    }
+} // namespace O3DE::ProjectManager

+ 109 - 0
Code/Tools/ProjectManager/Source/FormLineEditTagsWidget.h

@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#if !defined(Q_MOC_RUN)
+#include <QWidget>
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QCompleter)
+QT_FORWARD_DECLARE_CLASS(QPushButton)
+QT_FORWARD_DECLARE_CLASS(QLineEdit)
+QT_FORWARD_DECLARE_CLASS(QLabel)
+QT_FORWARD_DECLARE_CLASS(QFrame)
+QT_FORWARD_DECLARE_CLASS(QMovie)
+QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
+QT_FORWARD_DECLARE_CLASS(QMouseEvent)
+QT_FORWARD_DECLARE_CLASS(QKeyEvent)
+
+namespace AzQtComponents
+{
+    class StyledLineEdit;
+}
+
+namespace O3DE::ProjectManager
+{
+    class FormLineEditTagsWidget
+        : public QWidget 
+    {
+        Q_OBJECT
+
+    public:
+
+        enum class ValidationState
+        {
+            NotValidating,
+            Validating,
+            ValidationFailed,
+            ValidationSuccess
+        };
+
+        FormLineEditTagsWidget(
+            const QString& labelText,
+            const QString& valueText,
+            const QString& placeholderText,
+            const QString& errorText,
+            QWidget* parent = nullptr);
+        explicit FormLineEditTagsWidget(const QString& labelText, const QString& valueText = "", QWidget* parent = nullptr);
+        ~FormLineEditTagsWidget() = default;
+
+        //! Set the error message for to display when invalid.
+        void setErrorLabelText(const QString& labelText);
+        void setErrorLabelVisible(bool visible);
+
+        //! Returns a pointer to the underlying LineEdit.
+        QLineEdit* lineEdit() const;
+
+        virtual void setText(const QString& text);
+
+        void SetValidationState(ValidationState validationState);
+
+        QStringList getTags()
+        {
+            return m_tags;
+        }
+
+    protected:
+        QLabel* m_errorLabel = nullptr;
+        QFrame* m_frame = nullptr;
+        QHBoxLayout* m_frameLayout = nullptr;
+        AzQtComponents::StyledLineEdit* m_lineEdit = nullptr;
+        QPushButton* m_dropdownButton = nullptr;
+        QFrame* m_tagFrame = nullptr;
+        
+        //Validation icons
+        QMovie* m_processingSpinnerMovie = nullptr;
+        QLabel* m_processingSpinner = nullptr;
+        QLabel* m_validationErrorIcon = nullptr;
+        QLabel* m_validationSuccessIcon = nullptr;
+        ValidationState m_validationState = ValidationState::NotValidating;
+
+    private slots:
+        void flavorChanged();
+        void onFocus();
+        void onFocusOut();
+        void textChanged(const QString& text);
+        void processTagDelete(int unused);
+
+    private:
+        void mousePressEvent(QMouseEvent* event) override;
+        void keyPressEvent(QKeyEvent* event) override;
+        void refreshStyle();
+        void setupCompletionTags();
+        void refreshTagFrame();
+        void forceSetText(const QString& text);
+        void forceSubmitCurrentText();
+        void addToTagList(const QString& text);
+
+        QStringList m_completionTags;
+        QStringList m_tags;
+        QCompleter* m_completer;
+
+    };
+} // namespace O3DE::ProjectManager

+ 2 - 0
Code/Tools/ProjectManager/project_manager_files.cmake

@@ -21,6 +21,8 @@ set(FILES
     Source/EngineInfo.cpp
     Source/FormLineEditWidget.h
     Source/FormLineEditWidget.cpp
+    Source/FormLineEditTagsWidget.h
+    Source/FormLineEditTagsWidget.cpp
     Source/FormBrowseEditWidget.h
     Source/FormBrowseEditWidget.cpp
     Source/FormComboBoxWidget.h