diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp index 2d3a2e9678..777961d401 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp @@ -45,6 +45,35 @@ namespace AdaptiveCardQmlEngine return mCardConfig; } + void AdaptiveCardContext::addHeightEstimate(const int height) + { + m_HeightEstimate += height; + } + + void AdaptiveCardContext::setHeightEstimate(const int height) + { + m_HeightEstimate = height; + } + + const int AdaptiveCardContext::getHeightEstimate() + { + return m_HeightEstimate; + } + + const int AdaptiveCardQmlEngine::AdaptiveCardContext::getEstimatedTextHeight(const std::string text) + { + auto cardConfig = this->getCardConfig()->getCardConfig(); + + int height = 0; + + height += ((((int(text.size()) * cardConfig.averageCharWidth) / cardConfig.cardWidth) + 1) * cardConfig.averageCharHeight); + height += (int(std::count(text.begin(), text.end(), '\n')) * cardConfig.averageCharHeight); + height += cardConfig.averageSpacing; + + return height; + } + + QString AdaptiveCardContext::getColor(AdaptiveCards::ForegroundColor color, bool isSubtle, bool highlight, bool isQml) { AdaptiveCards::ColorConfig colorConfig; @@ -98,5 +127,15 @@ namespace AdaptiveCardQmlEngine m_lang = lang; } + const std::vector& AdaptiveCardContext::GetWarnings() + { + return m_warnings; + } + + void AdaptiveCardContext::AddWarning(const AdaptiveWarning& warning) + { + m_warnings.push_back(warning); + } + } // namespace AdaptiveCardQmlEngine diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h index 8b28e6f0d5..07728d9cf1 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h @@ -12,6 +12,7 @@ #include "Formatter.h" #include "utils/Utils.h" #include "utils/AdaptiveCardEnums.h" +#include "AdaptiveWarning.h" namespace AdaptiveCardQmlEngine { @@ -37,11 +38,21 @@ namespace AdaptiveCardQmlEngine std::shared_ptr getHostConfig(); std::shared_ptr getCardConfig(); + void addHeightEstimate(const int height); + void setHeightEstimate(const int height); + const int getHeightEstimate(); + + const int getEstimatedTextHeight(const std::string text); + QString getColor(AdaptiveCards::ForegroundColor color, bool isSubtle, bool highlight, bool isQml = true); std::string getLang(); void setLang(const std::string& lang); + const std::vector& GetWarnings(); + void AddWarning(const AdaptiveWarning& warning); + + private: AdaptiveCardContext(); ~AdaptiveCardContext(); @@ -54,6 +65,8 @@ namespace AdaptiveCardQmlEngine std::shared_ptr mHostConfig; std::shared_ptr mCardConfig; AdaptiveCardEnums::AdaptiveCardTheme mAdaptiveCardTheme; - std::string m_lang; + std::string m_lang; + int m_HeightEstimate{0}; + std::vector m_warnings; }; } diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h index 168a931b58..c364036035 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h @@ -23,5 +23,10 @@ namespace AdaptiveCardQmlEngine qmlRegisterType(QUrl("qrc:qml/CardConstants.qml"), "AdaptiveCardQmlEngine", 1, 0, "CardConstants"); qmlRegisterType(QUrl("qrc:qml/TextBlockRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "TextBlockRender"); + qmlRegisterType(QUrl("qrc:qml/SingleLineTextInputRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "SingleLineTextInputRender"); + qmlRegisterType(QUrl("qrc:qml/InputLabel.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputLabel"); + qmlRegisterType(QUrl("qrc:qml/InputErrorMessage.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputErrorMessage"); + qmlRegisterType(QUrl("qrc:qml/InputFieldClearIcon.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputFieldClearIcon"); + } } // namespace RendererQml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt index 82fe7bada8..2c1a46009b 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt +++ b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt @@ -53,10 +53,12 @@ file(GLOB_RECURSE SOURCES "AdaptiveCardController.cpp" "AdaptiveCardModel.cpp" "CollectionItemModel.cpp" + "AdaptiveWarning.cpp" "TextBlockModel.cpp" "ImageModel.cpp" "RichTextBlockModel.cpp" + "TextInputModel.cpp" "AdaptiveCardQmlTypes.h" "AdaptiveCardUtils.cpp" @@ -69,15 +71,16 @@ file(GLOB_RECURSE SOURCES "Formatter.h" "Utils.h" "stdafx.h" + + "AdaptiveCardController.h" + "AdaptiveCardModel.h" + "CollectionItemModel.h" + "AdaptiveWarning.h" - "AdaptiveCardController.h" - "AdaptiveCardModel.h" - "CollectionItemModel.h" - - "TextBlockModel.h" + "TextBlockModel.h" + "RichTextBlockModel.h" + "TextInputModel.h" "ImageModel.h" - "TextBlockModel.h" - "RichTextBlockModel.h" ) # Setup Library diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.cpp new file mode 100644 index 0000000000..b6b91c1dd3 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.cpp @@ -0,0 +1,24 @@ +#include "pch.h" + +#include "AdaptiveWarning.h" + +namespace AdaptiveCardQmlEngine +{ + + AdaptiveWarning::AdaptiveWarning(Code code, const std::string& message) : + m_code(code), m_message(message) + { + + } + + Code AdaptiveWarning::GetStatusCode() const + { + return m_code; + } + + const std::string& AdaptiveWarning::GetReason() const + { + return m_message; + } + +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.h b/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.h new file mode 100644 index 0000000000..599c27deca --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.h @@ -0,0 +1,25 @@ +#pragma once + +#include "pch.h" + +namespace AdaptiveCardQmlEngine +{ + + enum class Code + { + RenderException = 1 + }; + + class AdaptiveWarning + { + public: + AdaptiveWarning(Code code, const std::string& message); + Code GetStatusCode() const; + const std::string& GetReason() const; + + private: + const Code m_code; + const std::string m_message; + }; + +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp index f570d92bc0..1819c7e25c 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp @@ -2,6 +2,7 @@ #include "TextBlockModel.h" #include "ImageModel.h" #include "RichTextBlockModel.h" +#include "TextInputModel.h" #include "AdaptiveCardEnums.h" CollectionItemModel::CollectionItemModel(std::vector> elements, QObject* parent) @@ -43,6 +44,7 @@ QHash CollectionItemModel::roleNames() const cardListModel[TextBlockRole] = "textBlockRole"; cardListModel[ImageRole] = "imageRole"; cardListModel[RichTextBlockRole] = "richTextBlockRole"; + cardListModel[TextInputRole] = "textInputRole"; cardListModel[FillHeightRole] = "fillHeightRole"; return cardListModel; @@ -64,6 +66,9 @@ void CollectionItemModel::populateRowData(std::shared_ptr(element), rowContent); break; + case AdaptiveCards::CardElementType::TextInput: + populateTextInputModel(std::dynamic_pointer_cast(element), rowContent); + break; default: break; } @@ -86,3 +91,9 @@ void CollectionItemModel::populateRichTextBlockModel(std::shared_ptr input, RowContent& rowContent) +{ + rowContent[CollectionModelRole::DelegateType] = QVariant::fromValue(AdaptiveCardEnums::CardElementType::TextInput); + rowContent[CollectionModelRole::TextInputRole] = QVariant::fromValue(new TextInputModel(input, nullptr)); +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h index eb71dfaa2c..67c47c7d7c 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h @@ -10,11 +10,13 @@ class TextBlockModel; class ImageModel; #include "RichTextBlock.h" +#include #include "Enums.h" class TextBlockModel; class RichTextBlockModel; +class TextIputModel; class CollectionItemModel : public QAbstractListModel { @@ -26,6 +28,7 @@ class CollectionItemModel : public QAbstractListModel TextBlockRole, ImageRole, RichTextBlockRole, + TextInputRole, FillHeightRole }; @@ -48,4 +51,5 @@ class CollectionItemModel : public QAbstractListModel void populateTextBlockModel(std::shared_ptr textBlock, RowContent& rowContent); void populateImageModel(std::shared_ptr image, RowContent& rowContent); void populateRichTextBlockModel(std::shared_ptr rightTextBlock, RowContent& rowContent); + void populateTextInputModel(std::shared_ptr input, RowContent& rowContent); }; diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.cpp new file mode 100644 index 0000000000..b45a0df104 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.cpp @@ -0,0 +1,52 @@ +#include "TextInputModel.h" +#include "SharedAdaptiveCard.h" +#include +#include "Utils.h" +#include "MarkDownParser.h" +#include "AdaptiveWarning.h" + +TextInputModel::TextInputModel(std::shared_ptr input, QObject* parent) : QObject(parent) +{ + const auto hostConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getHostConfig(); + const auto rendererConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getCardConfig(); + + const auto textConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getCardConfig()->getInputTextConfig(); + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().addHeightEstimate(AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getEstimatedTextHeight(input->GetLabel())); + + std::string mOriginalElementId = input->GetId(); + + mIsVisible = input->GetIsVisible() ? true : false; + mIsRequired = input->GetIsRequired() == true ? true : false; + + mLabel = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(input->GetLabel())); + mErrorMessage = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(input->GetErrorMessage())); + + mPlaceHolder = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(input->GetPlaceholder())); + mValue = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(input->GetValue())); + + auto spacing = AdaptiveCardQmlEngine::Utils::GetSpacing(AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getHostConfig()->GetSpacing(), AdaptiveCards::Spacing::Small); + QString color = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getColor(AdaptiveCards::ForegroundColor::Default, false, false); + + if (input->GetRegex() != "") + { + mRegex = QString::fromStdString(input->GetRegex()); + } + + mMaxLength = input->GetMaxLength(); + + if (input->GetLabel().empty()) + { + if (input->GetIsRequired()) + { + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().AddWarning(AdaptiveCardQmlEngine::AdaptiveWarning(AdaptiveCardQmlEngine::Code::RenderException, "isRequired is not supported without labels")); + } + } + + if (input->GetHeight() == AdaptiveCards::HeightType::Stretch) + { + mHeightStreched = input->GetHeight() == AdaptiveCards::HeightType::Stretch ? "true" : "false"; + } +} + +TextInputModel::~TextInputModel() +{} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.h new file mode 100644 index 0000000000..af5233baf9 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/TextInputModel.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include + +#include "AdaptiveCardContext.h" +#include "TextInput.h" + +class TextInputModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString label MEMBER mLabel CONSTANT) + Q_PROPERTY(QString errorMessage MEMBER mErrorMessage CONSTANT) + Q_PROPERTY(QString placeholder MEMBER mPlaceHolder CONSTANT) + Q_PROPERTY(QString value MEMBER mValue CONSTANT) + Q_PROPERTY(QString regex MEMBER mRegex CONSTANT) + + Q_PROPERTY(bool isVisible MEMBER mIsVisible CONSTANT) + Q_PROPERTY(bool isRequired MEMBER mIsRequired CONSTANT) + Q_PROPERTY(bool heightStreched MEMBER mHeightStreched CONSTANT) + + Q_PROPERTY(int maxLength MEMBER mMaxLength CONSTANT) + +public: + explicit TextInputModel(std::shared_ptr input, QObject* parent = nullptr); + ~TextInputModel(); + +private: + QString mLabel; + QString mErrorMessage; + QString mPlaceHolder; + QString mValue; + QString mRegex; + + bool mIsVisible; + bool mIsRequired; + bool mHeightStreched; + + int mMaxLength; +}; diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/CardConstants.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/CardConstants.qml index 720a6bd9e4..661ac845c7 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/qml/CardConstants.qml +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/CardConstants.qml @@ -1,6 +1,6 @@ -import "AdaptiveCardUtils.js" as AdaptiveCardUtils +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils import QtQuick 2.15 -pragma Singleton +//pragma Singleton QtObject { property bool isDarkTheme: false diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml index 99c2165994..c86d3113b9 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml @@ -14,5 +14,7 @@ Loader { source = "ImageRender.qml" else if (model.delegateType == AdaptiveCardEnums.CardElementType.RichTextBlock) source = "RichTextBlockRender.qml"; + else if (model.delegateType == AdaptiveCardEnums.CardElementType.TextInput) + source = "TextInputRender.qml" } } diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/InputErrorMessage.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputErrorMessage.qml new file mode 100644 index 0000000000..b093e62de0 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputErrorMessage.qml @@ -0,0 +1,51 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Rectangle { + id: errorMessage + + property string errorMessage + + width: parent.width + height: errorMessageLabel.implicitHeight + color: 'transparent' + + Button { + id: errorIcon + + width: cardConst.inputFieldConstants.errorIconWidth + anchors.left: parent.left + anchors.leftMargin: cardConst.inputFieldConstants.errorIconLeftMargin + anchors.topMargin: cardConst.inputFieldConstants.errorIconTopMargin + horizontalPadding: 0 + verticalPadding: 0 + icon.width: cardConst.inputFieldConstants.errorIconWidth + icon.height: cardConst.inputFieldConstants.errorIconHeight + icon.color: cardConst.toggleButtonConstants.errorMessageColor + anchors.top: parent.top + icon.source: cardConst.errorIcon + enabled: false + + background: Rectangle { + color: 'transparent' + } + + } + + Label { + id: errorMessageLabel + + wrapMode: Text.Wrap + font.pixelSize: cardConst.inputFieldConstants.labelPixelSize + Accessible.ignored: true + color: cardConst.toggleButtonConstants.errorMessageColor + anchors.left: errorIcon.right + anchors.leftMargin: cardConst.inputFieldConstants.errorIconLeftMargin + anchors.right: parent.right + text: textInputModel.errorMessage//_errorMessage + } + +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml new file mode 100644 index 0000000000..70032da312 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml @@ -0,0 +1,32 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Button { + id: inputFieldClearIcon + + width: cardConst.inputFieldConstants.clearIconSize + horizontalPadding: 0 + verticalPadding: 0 + icon.width: cardConst.inputFieldConstants.clearIconSize + icon.height: cardConst.inputFieldConstants.clearIconSize + icon.color: cardConst.inputFieldConstants.clearIconColorNormal + icon.source: cardConst.clearIconImage + Accessible.name: qsTr("Clear Input") + Accessible.role: Accessible.Button + + background: Rectangle { + color: 'transparent' + radius: cardConst.inputFieldConstants.borderRadius + + WCustomFocusItem { + isRectangle: true + visible: inputFieldClearIcon.activeFocus + designatedParent: parent + } + + } + +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/InputLabel.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputLabel.qml new file mode 100644 index 0000000000..1b8a315ace --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputLabel.qml @@ -0,0 +1,18 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Label { + id: inputLabel + + property bool required + + wrapMode: Text.Wrap + width: parent.width + color: cardConst.toggleButtonConstants.textColor + font.pixelSize: cardConst.inputFieldConstants.labelPixelSize + Accessible.ignored: true + text: textInputModel.isRequired ? AdaptiveCardUtils.escapeHtml(textInputModel.label) + " " + "*" : AdaptiveCardUtils.escapeHtml(textInputModel.label) +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/SingleLineTextInputRender.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/SingleLineTextInputRender.qml new file mode 100644 index 0000000000..fe3cf481f7 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/SingleLineTextInputRender.qml @@ -0,0 +1,109 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Rectangle { + id: singleLineTextElementRect + + property bool errorMessageVisible + property string textValue: inputtextTextField.text + + border.color: inputtextTextField.outerShowErrorMessage ? cardConst.inputFieldConstants.borderColorOnError : cardConst.inputFieldConstants.borderColorNormal + border.width: cardConst.inputFieldConstants.borderWidth + radius: cardConst.inputFieldConstants.borderRadius + color: cardConst.inputFieldConstants.backgroundColorNormal + height: cardConst.inputFieldConstants.height + + TextField { + id: inputtextTextField + + property bool outerShowErrorMessage: errorMessageVisible + + function colorChange(colorItem, focusItem, isPressed) { + if (isPressed && !focusItem.outerShowErrorMessage) + colorItem.color = cardConst.inputFieldConstants.backgroundColorOnPressed; + else + colorItem.color = focusItem.activeFocus ? cardConst.inputFieldConstants.backgroundColorOnPressed : focusItem.hovered ? cardConst.inputFieldConstants.backgroundColorOnHovered : cardConst.inputFieldConstants.backgroundColorNormal ; + } + + function assignMaxLength() { + if (textInputModel.maxLength != 0) + maximumLength = textInputModel.maxLength; + + } + + selectByMouse: true + selectedTextColor: 'white' + color: cardConst.inputFieldConstants.textColor + placeholderTextColor: cardConst.inputFieldConstants.placeHolderColor + Accessible.role: Accessible.EditableText + + onPressed: { + colorChange(inputtextTextFieldWrapper, inputtextTextField, true); + event.accepted = true; + } + onReleased: { + colorChange(inputtextTextFieldWrapper, inputtextTextField, false); + forceActiveFocus(); + event.accepted = true; + } + + onHoveredChanged: colorChange(inputtextTextFieldWrapper, inputtextTextField, false) + onActiveFocusChanged: { + colorChange(inputtextTextFieldWrapper, inputtextTextField, false); + if (activeFocus) + Accessible.name = getAccessibleName(); + } + + onOuterShowErrorMessageChanged: { + if (textInputModel.isRequired || textInputModel.regex != "") + colorChange(inputtextTextFieldWrapper, inputtextTextField, false); + } + + Accessible.name: "" + leftPadding: cardConst.inputFieldConstants.textHorizontalPadding + rightPadding: cardConst.inputFieldConstants.textHorizontalPadding + topPadding: cardConst.inputFieldConstants.textVerticalPadding + bottomPadding: cardConst.inputFieldConstants.textVerticalPadding + padding: 0 + font.pixelSize: cardConst.inputFieldConstants.pixelSize + text: textInputModel.value + placeholderText: activeFocus ? '' : textInputModel.placeholder + width: parent.width + + onTextChanged: { + if (textInputModel.maxLength != 0) + remove(textInputModel.maxLength, length); + } + Component.onCompleted: { + assignMaxLength(); + } + + background: Rectangle { + color: 'transparent' + } + } + + InputFieldClearIcon { + id: inputtextTextFieldClearIcon + + Keys.onReturnPressed: onClicked() + visible: inputtextTextField.text.length != 0 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: cardConst.inputFieldConstants.clearIconHorizontalPadding + onClicked: { + nextItemInFocusChain().forceActiveFocus(); + inputtextTextField.clear(); + } + } + + WCustomFocusItem { + isRectangle: true + visible: inputtextTextField.activeFocus + designatedParent: parent + } + +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/TextInputRender.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/TextInputRender.qml new file mode 100644 index 0000000000..4f0e9daa3d --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/TextInputRender.qml @@ -0,0 +1,111 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Column { + id: inputText + + CardConstants { + id: cardConst + } + + property var textInputModel: model.textInputRole + + property bool isMultiLineText + property string submitValue: !isMultiLineText ? singlineLoaderElement.item.textValue : multilineLoaderElement.item.textValue + property bool showErrorMessage: false + + function validate() { + const regex = new RegExp(textInputModel.regex); + let isValid = true; + + if (textInputModel.isRequired) + isValid = (submitValue !== '' && regex.test(submitValue)); + else if (submitValue !== '') + isValid = regex.test(submitValue); + + if (showErrorMessage && isValid) + showErrorMessage = false; + + return !isValid; + } + + function getAccessibleName() { + let accessibleName = ''; + if (showErrorMessage === true) + accessibleName += "Error" + textInputModel.errorMessage; + + accessibleName += textInputModel.label; + if (submitValue !== '') + accessibleName += (submitValue + '. '); + else + accessibleName += textInputModel.placeholder; + + accessibleName += qsTr(", Type the text"); + return accessibleName; + } + + visible: textInputModel.isVisible + spacing: textInputModel.spacing + width: parent.width + onActiveFocusChanged: { + if (activeFocus) + nextItemInFocusChain().forceActiveFocus(); + + } + onSubmitValueChanged: { + if (textInputModel.isRequired || textInputModel.regex != "") + validate(); + + } + onShowErrorMessageChanged: { + if (textInputModel.heightStreched && isMultiLineText) { + if (showErrorMessage) + inputtextTextFieldRow.height = inputtextTextFieldRow.height - inputtextErrorMessage.height; + else + inputtextTextFieldRow.height = inputtextTextFieldRow.height + inputtextErrorMessage.height; + } + } + + InputLabel { + id: inputTextLabel + + required: textInputModel.isRequired + } + + Row { + id: inputtextTextFieldRow + + function getStrechHeight() { + if (textInputModel.heightStreched && isMultiLineText) + return parent.height > 0 ? parent.height - y : CardConstants.inputTextConstants.multiLineTextHeight; + } + + spacing: 5 + width: parent.width + height: implicitHeight + + Loader { + id: singlineLoaderElement + + height: 30 + width: parent.width + active: !isMultiLineText + + sourceComponent: SingleLineTextInputRender { + id: inputtextTextFieldWrapper + + errorMessageVisible: showErrorMessage + } + } + } + + InputErrorMessage { + id: inputtextErrorMessage + + errorMessage: textInputModel.errorMessage + visible: showErrorMessage + } +} \ No newline at end of file diff --git a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc index c23bc8b2b0..d920d55b5a 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc +++ b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc @@ -16,5 +16,10 @@ images/3.jpg images/4.jpg qml/RichTextBlockRender.qml + qml/TextInputRender.qml + qml/InputErrorMessage.qml + qml/InputFieldClearIcon.qml + qml/InputLabel.qml + qml/SingleLineTextInputRender.qml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h b/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h index 89e4a43806..1f86394bf5 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h @@ -10,6 +10,7 @@ namespace AdaptiveCardEnums TextBlock, Image, RichTextBlock, + TextInput, Container, Column, ColumnSet, diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp index 4400f3373c..6d5c433d24 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp @@ -208,6 +208,44 @@ namespace AdaptiveCardQmlEngine return true; } + std::string AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(std::string str) + { + std::string rawString = ""; + for (int i = 0; i < str.size(); i++) + { + if (str[i] == '`') + { + rawString += "${'`'}"; + } + else + { + rawString += str[i]; + } + } + return rawString; + } + + int Utils::GetSpacing(const AdaptiveCards::SpacingConfig& spacingConfig, const AdaptiveCards::Spacing spacing) + { + switch (spacing) + { + case AdaptiveCards::Spacing::None: + return 0; + case AdaptiveCards::Spacing::Small: + return spacingConfig.smallSpacing; + case AdaptiveCards::Spacing::Medium: + return spacingConfig.mediumSpacing; + case AdaptiveCards::Spacing::Large: + return spacingConfig.largeSpacing; + case AdaptiveCards::Spacing::ExtraLarge: + return spacingConfig.extraLargeSpacing; + case AdaptiveCards::Spacing::Padding: + return spacingConfig.paddingSpacing; + default: + return spacingConfig.defaultSpacing; + } + } + } // namespace AdaptiveCardQmlEngine diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h index d9a3ecd835..95b44279be 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h @@ -17,6 +17,9 @@ namespace AdaptiveCardQmlEngine static const std::string formatHtmlUrl(std::string& text, const std::string& linkColor, const std::string& textDecoration); static std::vector splitString(const std::string& string, char delimiter); static std::string& replace(std::string& str, char what, char with); + + static std::string getBackQuoteEscapedString(std::string str); + static int GetSpacing(const AdaptiveCards::SpacingConfig& spacingConfig, const AdaptiveCards::Spacing spacing); template static bool IsInstanceOfSmart(U u);