From 544e57349f14b544ceed541394168a70b7639394 Mon Sep 17 00:00:00 2001 From: sachinrajauriya Date: Sun, 25 Aug 2024 18:30:43 +0530 Subject: [PATCH 1/3] Changes For ImageRenderer --- .../AdaptiveCardQmlEngine/CMakeLists.txt | 2 + .../qml_v2/AdaptiveCardQmlEngine/images/0.jpg | Bin 0 -> 10781 bytes .../qml_v2/AdaptiveCardQmlEngine/images/1.jpg | Bin 0 -> 10781 bytes .../qml_v2/AdaptiveCardQmlEngine/images/2.jpg | Bin 0 -> 10781 bytes .../qml_v2/AdaptiveCardQmlEngine/images/3.jpg | Bin 0 -> 10781 bytes .../qml_v2/AdaptiveCardQmlEngine/images/4.jpg | Bin 0 -> 10781 bytes .../models/CollectionItemModel.cpp | 11 ++ .../models/CollectionItemModel.h | 4 + .../models/ImageModel.cpp | 135 ++++++++++++++++++ .../AdaptiveCardQmlEngine/models/ImageModel.h | 62 ++++++++ .../qml/CollectionItemDelegate.qml | 26 +--- .../AdaptiveCardQmlEngine/qml/ImageRender.qml | 93 ++++++++++++ .../AdaptiveCardQmlEngine/resourceEngine.qrc | 6 + .../utils/AdaptiveCardEnums.h | 1 + 14 files changed, 320 insertions(+), 20 deletions(-) create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/images/0.jpg create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/images/1.jpg create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/images/2.jpg create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/images/3.jpg create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/images/4.jpg create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.cpp create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.h create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/qml/ImageRender.qml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt index 0cff0beadb..9906590343 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt +++ b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt @@ -55,6 +55,7 @@ file(GLOB_RECURSE SOURCES "CollectionItemModel.cpp" "TextBlockModel.cpp" + "ImageModel.cpp" "AdaptiveCardQmlTypes.h" "AdaptiveCardUtils.cpp" @@ -73,6 +74,7 @@ file(GLOB_RECURSE SOURCES "CollectionItemModel.h" "TextBlockModel.h" + "ImageModel.h" ) # Setup Library diff --git a/source/qml_v2/AdaptiveCardQmlEngine/images/0.jpg b/source/qml_v2/AdaptiveCardQmlEngine/images/0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2703578c631886bf6a35b5ba6132c525eb9a3671 GIT binary patch literal 10781 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 literal 0 HcmV?d00001 diff --git a/source/qml_v2/AdaptiveCardQmlEngine/images/1.jpg b/source/qml_v2/AdaptiveCardQmlEngine/images/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2703578c631886bf6a35b5ba6132c525eb9a3671 GIT binary patch literal 10781 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 literal 0 HcmV?d00001 diff --git a/source/qml_v2/AdaptiveCardQmlEngine/images/2.jpg b/source/qml_v2/AdaptiveCardQmlEngine/images/2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2703578c631886bf6a35b5ba6132c525eb9a3671 GIT binary patch literal 10781 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 literal 0 HcmV?d00001 diff --git a/source/qml_v2/AdaptiveCardQmlEngine/images/3.jpg b/source/qml_v2/AdaptiveCardQmlEngine/images/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2703578c631886bf6a35b5ba6132c525eb9a3671 GIT binary patch literal 10781 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 literal 0 HcmV?d00001 diff --git a/source/qml_v2/AdaptiveCardQmlEngine/images/4.jpg b/source/qml_v2/AdaptiveCardQmlEngine/images/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2703578c631886bf6a35b5ba6132c525eb9a3671 GIT binary patch literal 10781 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 literal 0 HcmV?d00001 diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp index 2284009ab6..c434ad85d6 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.cpp @@ -1,5 +1,6 @@ #include "CollectionItemModel.h" #include "TextBlockModel.h" +#include "ImageModel.h" #include "AdaptiveCardEnums.h" CollectionItemModel::CollectionItemModel(std::vector> elements, QObject* parent) @@ -39,6 +40,7 @@ QHash CollectionItemModel::roleNames() const QHash cardListModel; cardListModel[DelegateType] = "delegateType"; cardListModel[TextBlockRole] = "textBlockRole"; + cardListModel[ImageRole] = "imageRole"; cardListModel[FillHeightRole] = "fillHeightRole"; return cardListModel; @@ -54,6 +56,10 @@ void CollectionItemModel::populateRowData(std::shared_ptr(element), rowContent); break; + case AdaptiveCards::CardElementType::Image: + populateImageModel(std::dynamic_pointer_cast(element), rowContent); + break; + default: break; } @@ -66,3 +72,8 @@ void CollectionItemModel::populateTextBlockModel(std::shared_ptr image, RowContent& rowContent) +{ + rowContent[CollectionModelRole::DelegateType] = QVariant::fromValue(AdaptiveCardEnums::CardElementType::Image); + rowContent[CollectionModelRole::ImageRole] = QVariant::fromValue(new ImageModel(image, nullptr)); +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h index 47e7f3e72e..bcc0eb38d8 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/CollectionItemModel.h @@ -4,9 +4,11 @@ #include #include +#include #include "Enums.h" class TextBlockModel; +class ImageModel; class CollectionItemModel : public QAbstractListModel { @@ -16,6 +18,7 @@ class CollectionItemModel : public QAbstractListModel { DelegateType = Qt::UserRole + 1, TextBlockRole, + ImageRole, FillHeightRole }; @@ -36,4 +39,5 @@ class CollectionItemModel : public QAbstractListModel private: void populateRowData(std::shared_ptr element); void populateTextBlockModel(std::shared_ptr textBlock, RowContent& rowContent); + void populateImageModel(std::shared_ptr image, RowContent& rowContent); }; diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.cpp new file mode 100644 index 0000000000..81b3f3aa72 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.cpp @@ -0,0 +1,135 @@ +#include "ImageModel.h" +#include "SharedAdaptiveCard.h" +#include +#include "Utils.h" +#include "MarkDownParser.h" + + +ImageModel::ImageModel(std::shared_ptr image, QObject* parent) + : QObject(parent), + mSourceImage(""), mImageHeight(0), mImageWidth(0), mRadius(0), + mIsImage(false), mVisibleRect(false), mBgColor("transparent"), + mAnchorCenter(""), mAnchorRight(""), mAnchorLeft("") +{ + const auto hostConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getHostConfig(); + const auto rendererConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getCardConfig(); + + mSourceImage = GetImagePath(image->GetUrl()); + mVisibleRect = image->GetIsVisible(); + mIsImage = true; + + // If the image has a pixel width and height, use that else calculate the width and height based on the image size + if (image->GetPixelWidth() != 0 || image->GetPixelHeight() != 0) + { + mImageHeight = image->GetPixelHeight() != 0 ? image->GetPixelHeight() : image->GetPixelWidth(); + mImageWidth = image->GetPixelWidth() != 0 ? image->GetPixelWidth() : image->GetPixelHeight(); + } + else + { + switch (image->GetImageSize()) + { + case AdaptiveCards::ImageSize::None: + case AdaptiveCards::ImageSize::Auto: + case AdaptiveCards::ImageSize::Small: + + mImageWidth = hostConfig->GetImageSizes().smallSize; + mImageHeight = hostConfig->GetImageSizes().smallSize; + break; + + case AdaptiveCards::ImageSize::Medium: + + mImageWidth = hostConfig->GetImageSizes().mediumSize; + mImageHeight = hostConfig->GetImageSizes().mediumSize; + break; + + case AdaptiveCards::ImageSize::Large: + mImageWidth = hostConfig->GetImageSizes().largeSize; + mImageHeight = hostConfig->GetImageSizes().largeSize; + break; + } + + } + + // Image Background Color + if (!image->GetBackgroundColor().empty()) + { + mBgColor = QString::fromStdString(image->GetBackgroundColor()); + } + + // Image Horizontal Alignment + const auto imageHorizontalAlignment = image->GetHorizontalAlignment().value_or(AdaptiveCards::HorizontalAlignment::Left); + switch (imageHorizontalAlignment) + { + case AdaptiveCards::HorizontalAlignment::Left: + mAnchorLeft = "left"; + break; + case AdaptiveCards::HorizontalAlignment::Center: + mAnchorCenter = "center"; + break; + case AdaptiveCards::HorizontalAlignment::Right: + mAnchorRight = "right"; + break; + default: + mAnchorLeft = "left"; + break; + } + + // Image Style + switch (image->GetImageStyle()) + { + case AdaptiveCards::ImageStyle::Default: + break; + case AdaptiveCards::ImageStyle::Person: + mRadius = mImageWidth/2; + break; + } + + // To Do: Need to implement the actions + // Image selection action + if (image->GetSelectAction() != nullptr) + { + bool mHoverEnabled = true; + if (image->GetSelectAction()->GetElementTypeString() == "Action.Submit") + { + auto submitAction = std::dynamic_pointer_cast(image->GetSelectAction()); + mActionType = QString::fromStdString(submitAction->GetElementTypeString()); + mSubmitJSON = QString::fromStdString(submitAction->GetDataJson()); + mHasAssociatedInputs = submitAction->GetAssociatedInputs() == AdaptiveCards::AssociatedInputs::Auto ? true : false; + } + else if (image->GetSelectAction()->GetElementTypeString() == "Action.OpenUrl") + { + auto openUrlAction = std::dynamic_pointer_cast(image->GetSelectAction()); + mActionType = QString::fromStdString(openUrlAction->GetElementTypeString()); + mOpenUrl = QString::fromStdString(openUrlAction->GetUrl()); + } + else if (image->GetSelectAction()->GetElementTypeString() == "Action.ToggleVisibility") + { + auto toggleVisibilityAction = std::dynamic_pointer_cast(image->GetSelectAction()); + mActionType = QString::fromStdString(toggleVisibilityAction->GetElementTypeString()); + mToggleVisibility = !mToggleVisibility; + } + } +} + +QString ImageModel::GetImagePath(const std::string url) +{ + // To Do: Need to download the file and save it in a local forder called "images" first. + // For now images are saved manually prior to render the image from local path. + + // Extracting the file name from the url + QString newUrl = QString::fromStdString(url); + const auto imageName = newUrl.split("://").at(1).split("/").last().split(".").first() + ".jpg"; + + // Setting up the local path for the image + QString file_path = __FILE__; + QString dir_path = file_path.left(file_path.lastIndexOf("\\models")); + dir_path.append("\\images\\" + imageName); + std::replace(dir_path.begin(), dir_path.end(), '\\', '/'); + dir_path = "file:/" + dir_path; + + return dir_path; +} + +ImageModel::~ImageModel() +{ +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.h new file mode 100644 index 0000000000..964845a523 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/ImageModel.h @@ -0,0 +1,62 @@ +#pragma once +#include "AdaptiveCardContext.h" +#include "Image.h" +#include "OpenUrlAction.h" +#include "ToggleVisibilityAction.h" +#include "SubmitAction.h" + +#include +#include +#include +#include + +class ImageModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString sourceImage MEMBER mSourceImage CONSTANT); + Q_PROPERTY(QString bgColor MEMBER mBgColor CONSTANT); + Q_PROPERTY(QString anchorCenter MEMBER mAnchorCenter CONSTANT); + Q_PROPERTY(QString anchorRight MEMBER mAnchorRight CONSTANT); + Q_PROPERTY(QString anchorLeft MEMBER mAnchorLeft CONSTANT); + + Q_PROPERTY(QString actionType MEMBER mActionType CONSTANT); + Q_PROPERTY(QString submitData MEMBER mSubmitJSON CONSTANT); + Q_PROPERTY(QString openUrl MEMBER mOpenUrl CONSTANT); + + Q_PROPERTY(bool hasAssociatedInputs MEMBER mHasAssociatedInputs CONSTANT); + Q_PROPERTY(bool visibleRect MEMBER mVisibleRect CONSTANT); + Q_PROPERTY(bool isImage MEMBER mIsImage CONSTANT); + Q_PROPERTY(bool toggleVisibility MEMBER mToggleVisibility CONSTANT); + + Q_PROPERTY(int imageHeight MEMBER mImageHeight CONSTANT); + Q_PROPERTY(int imageWidth MEMBER mImageWidth CONSTANT); + Q_PROPERTY(int radius MEMBER mRadius CONSTANT); + +public: + explicit ImageModel(std::shared_ptr image, QObject* parent = nullptr); + ~ImageModel(); + +private: + QString GetImagePath(const std::string url); + +private : + QString mSourceImage; + QString mBgColor; + QString mAnchorCenter; + QString mAnchorRight; + QString mAnchorLeft; + + QString mActionType; + QString mSubmitJSON; + QString mOpenUrl; + + bool mHasAssociatedInputs; + bool mVisibleRect; + bool mIsImage; + bool mToggleVisibility; + + int mImageHeight; + int mImageWidth; + int mRadius; +}; diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml index 6116fcdff8..15171779be 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml @@ -7,24 +7,10 @@ Loader { property var parentCardItem - source: "qrc:qml/TextBlockRender.qml" - - /* - { - switch (delegateType) { - case AdaptiveCardEnums.CardElementType.TextBlock: - return "TextBlockRender.qml" - case AdaptiveCardEnums.CardElementType.Container: - return "ContainerRender.qml" - case AdaptiveCardEnums.CardElementType.ColumnSet: - return "ColumnSetRender.qml" - case AdaptiveCardEnums.CardElementType.Column: - return "ColumnRender.qml" - case AdaptiveCardEnums.CardElementType.ToggleInput: - return "ToggleInputRender.qml" - case AdaptiveCardEnums.CardElementType.ActionSet: - return "ActionSetRender.qml" - } - } - */ + Component.onCompleted :{ + if (model.delegateType == AdaptiveCardEnums.CardElementType.TextBlock) + source = "TextBlockRender.qml" + else if (model.delegateType == AdaptiveCardEnums.CardElementType.Image) + source = "ImageRender.qml" + } } diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/ImageRender.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/ImageRender.qml new file mode 100644 index 0000000000..b7eb07006b --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/ImageRender.qml @@ -0,0 +1,93 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.15 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Item { + id: imageRender + property var imageModel: model.imageRole + + width: imageRect.width + height: imageRect.height + + Rectangle { + id: imageRect + + activeFocusOnTab: true + + width: imageModel.imageWidth != 0 ? imageModel.imageWidth : parent.width + height: imageModel.imageHeight != 0 ? imageModel.imageHeight : parent.width + + color: imageModel.bgColor != "" ? imageModel.bgColor : "transparent" + radius: imageModel.radius + + visible: imageModel.visibleRect + + anchors { + left: imageModel.anchorLeft ? parent.left : undefined + horizontalCenter: imageModel.anchorCenter ? parent.horizontalCenter : undefined + right: imageModel.anchorRight ? parent.right : undefined + } + + clip: true + layer.enabled: true + + layer.effect: OpacityMask { + readonly property bool isOpacityMask: true + + maskSource: Rectangle { + x: imageRender.x + y: imageRender.y + + width: imageRender.width + height: imageRender.height + + radius: imageRender.radius + } + + } + + Image { + id: image + + source:imageModel.sourceImage + anchors.fill: parent + + cache: false + visible: parent.visible + } + + MouseArea { + id: mouseArea + + function handleMouseAreaClick() { + if (model.actionType === "Action.ToggleVisibility") { + return ; + } else if (model.actionType === "Action.Submit") { + return ; + } else if(model.actionType === "Action.OpenUrl") { + return ; + } + } + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + hoverEnabled: model.isHoverEnabled + cursorShape: model.isHoverEnabled ? Qt.PointingHandCursor : Qt.ArrowCursor + + onClicked: { + handleMouseAreaClick(); + } + + Keys.onPressed: { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Space) { + handleMouseAreaClick(); + event.accepted = true; + } + } + } + } +} + diff --git a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc index ec6a6fe71b..ffcf1f9b03 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc +++ b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc @@ -9,5 +9,11 @@ qml/JSUtils/AdaptiveCardUtils.js qml/JSUtils/ThemeUtils.js qml/CardConstants.qml + qml/ImageRender.qml + images/0.jpg + images/1.jpg + images/2.jpg + images/3.jpg + images/4.jpg diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h b/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h index 0d2cd9c433..b3162db6a0 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h @@ -8,6 +8,7 @@ namespace AdaptiveCardEnums enum class CardElementType { AdaptiveCard, TextBlock, + Image, Container, Column, ColumnSet, From 9462059d6ada9d56cdd8066ab3aface8ec5bdc3b Mon Sep 17 00:00:00 2001 From: sachinrajauriya Date: Wed, 11 Sep 2024 23:58:00 +0530 Subject: [PATCH 2/3] NumberInput changes --- .../AdaptiveCardQmlTypes.h | 3 ++ .../AdaptiveCardQmlEngine/CMakeLists.txt | 4 +- .../models/NumberInputModel.cpp | 50 +++++++++++++++++++ .../models/NumberInputModel.h | 41 +++++++++++++++ .../qml/NumberInputRender.qml | 12 +++++ .../AdaptiveCardQmlEngine/resourceEngine.qrc | 1 + .../utils/AdaptiveCardEnums.h | 1 + 7 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h index 168a931b58..35228f05a7 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h @@ -23,5 +23,8 @@ 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/ImageRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "ImageRender"); + qmlRegisterType(QUrl("qrc:qml/RichTextBlockRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "RichTextBlockRender"); + qmlRegisterType(QUrl("qrc:qml/NumberInputRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "NumberInputRender"); } } // namespace RendererQml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt index 724ffc4224..3ffd9a8b7f 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt +++ b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt @@ -57,6 +57,7 @@ file(GLOB_RECURSE SOURCES "TextBlockModel.cpp" "ImageModel.cpp" "RichTextBlockModel.cpp" + "NumberInputModel.cpp" "AdaptiveCardQmlTypes.h" @@ -77,7 +78,8 @@ file(GLOB_RECURSE SOURCES "TextBlockModel.h" "ImageModel.h" - "RichTextBlockModel.h" + "RichTextBlockModel.h" + "NumberInputModel.h" ) # Setup Library diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp new file mode 100644 index 0000000000..67bb7f4471 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp @@ -0,0 +1,50 @@ +#include "NumberInputModel.h" +#include "SharedAdaptiveCard.h" +#include +#include "Utils.h" +#include "MarkDownParser.h" + +NumberInputModel::NumberInputModel(std::shared_ptr numberInput, QObject* parent) : + QObject(parent), mInput(numberInput) + +{ + const auto hostConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getHostConfig(); + const auto rendererConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getCardConfig(); + + mVisible = mInput->GetIsVisible(); + // mPlaceholder = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetPlaceholder()) + "`"); + + createInputLabel(); + + if (mInput->GetValue().has_value()) + { + mValue = mInput->GetValue().value(); + mDefaultValue = true; + } + + mMinValue = numberInput->GetMin().value_or(-DBL_MAX); + mMaxValue = numberInput->GetMax().value_or(DBL_MAX); + + if (numberInput->GetIsRequired() || numberInput->GetMin().has_value() || numberInput->GetMax().has_value()) + { + } +} +void NumberInputModel::createInputLabel() +{ + if (!mInput->GetLabel().empty()) + { + // mContext->addHeightEstimate(mContext->getEstimatedTextHeight(mInput->GetLabel())); + // mEscapedLabelString = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetLabel()) + "`"); + } + else + { + if (mInput->GetIsRequired()) + { + // mContext->AddWarning(RendererQml::AdaptiveWarning(RendererQml::Code::RenderException, "isRequired is not supported without labels")); + } + } +} + +NumberInputModel::~NumberInputModel() +{ +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h new file mode 100644 index 0000000000..e77a776aad --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h @@ -0,0 +1,41 @@ +#pragma once +#include "AdaptiveCardContext.h" +#include "NumberInput.h" +#include "OpenUrlAction.h" +#include "ToggleVisibilityAction.h" +#include "SubmitAction.h" + +#include +#include +#include +#include + +class NumberInputModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool visible MEMBER mVisible CONSTANT); + Q_PROPERTY(QString placeHolder MEMBER mPlaceholder CONSTANT); + Q_PROPERTY(QString value MEMBER mValue CONSTANT); + Q_PROPERTY(bool defaultValue MEMBER mDefaultValue CONSTANT); + Q_PROPERTY(QString minValue MEMBER mMinValue CONSTANT); + Q_PROPERTY(QString maxValue MEMBER mMaxValue CONSTANT); + Q_PROPERTY(QString escapedLabelString MEMBER mEscapedLabelString CONSTANT); + +public: + explicit NumberInputModel(std::shared_ptr numberInput, QObject* parent = nullptr); + ~NumberInputModel(); + +private: + void createInputLabel(); + const std::shared_ptr& mInput; + +private: + bool mVisible; + QString mPlaceholder; + QString mValue; + bool mDefaultValue; + QString mMinValue; + QString mMaxValue; + QString mEscapedLabelString; +}; diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml new file mode 100644 index 0000000000..0f4bf05c7c --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml @@ -0,0 +1,12 @@ +import QtQuick 2.3 + +Rectangle { + width: 200 + height: 100 + color: "red" + + Text { + anchors.centerIn: parent + text: "Hello, World!" + } +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc index c23bc8b2b0..29381eb22d 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc +++ b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc @@ -16,5 +16,6 @@ images/3.jpg images/4.jpg qml/RichTextBlockRender.qml + qml/NumberInputRender.qml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h b/source/qml_v2/AdaptiveCardQmlEngine/utils/AdaptiveCardEnums.h index 89e4a43806..688c102a88 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, + NumberInput, Container, Column, ColumnSet, From 6e0165050a25f0d1a6618a736e4d9b3c1581bd9d Mon Sep 17 00:00:00 2001 From: sachinrajauriya Date: Tue, 17 Sep 2024 22:32:41 +0530 Subject: [PATCH 3/3] NumberInput UI With Actual Data --- .../AdaptiveCardContext.cpp | 49 +++ .../AdaptiveCardContext.h | 22 +- .../AdaptiveCardQmlTypes.h | 3 + .../AdaptiveCardQmlEngine/CMakeLists.txt | 4 +- .../models/AdaptiveWarning.cpp | 24 ++ .../models/AdaptiveWarning.h | 25 ++ .../models/NumberInputModel.cpp | 50 ++- .../models/NumberInputModel.h | 37 ++- .../qml/CardConstants.qml | 4 +- .../qml/CollectionItemDelegate.qml | 16 +- .../qml/InputErrorMessage.qml | 48 +++ .../qml/InputFieldClearIcon.qml | 31 ++ .../AdaptiveCardQmlEngine/qml/InputLabel.qml | 16 + .../qml/NumberInputRender.qml | 288 +++++++++++++++++- .../AdaptiveCardQmlEngine/resourceEngine.qrc | 3 + .../AdaptiveCardQmlEngine/utils/Utils.cpp | 17 ++ .../AdaptiveCardQmlEngine/utils/Utils.h | 1 + .../JSONSamples/numberInput.json | 46 +++ .../SampleCardListModel.cpp | 2 +- 19 files changed, 641 insertions(+), 45 deletions(-) create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.cpp create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/models/AdaptiveWarning.h create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/qml/InputErrorMessage.qml create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml create mode 100644 source/qml_v2/AdaptiveCardQmlEngine/qml/InputLabel.qml create mode 100644 source/qml_v2/AdaptiveCardVisualizer/JSONSamples/numberInput.json diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp index 2d3a2e9678..e9e696d3d3 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.cpp @@ -98,5 +98,54 @@ namespace AdaptiveCardQmlEngine m_lang = lang; } + 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 mCardConfig = this->getCardConfig()->getCardConfig(); + + int height = 0; + + height += ((((int(text.size()) * mCardConfig.averageCharWidth) / mCardConfig.cardWidth) + 1) * mCardConfig.averageCharHeight); + height += (int(std::count(text.begin(), text.end(), '\n')) * mCardConfig.averageCharHeight); + height += mCardConfig.averageSpacing; + + return height; + } + + const std::vector& AdaptiveCardContext::GetWarnings() + { + return m_warnings; + } + + void AdaptiveCardContext::AddWarning(const AdaptiveWarning& warning) + { + m_warnings.push_back(warning); + } + + + void AdaptiveCardQmlEngine::AdaptiveCardContext::addToRequiredInputElementsIdList(const std::string& elementId) + { + m_RequiredInputElementsIdList.push_back(elementId); + } + + const std::vector& AdaptiveCardQmlEngine::AdaptiveCardContext::getRequiredInputElementsIdList() + { + return m_RequiredInputElementsIdList; + } + } // namespace AdaptiveCardQmlEngine diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h index 8b28e6f0d5..ba3c0882eb 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardContext.h @@ -7,6 +7,7 @@ #include "AdaptiveCardLightThemeConfig.h" #include "AdaptiveRenderArgs.h" +#include "AdaptiveWarning.h" #include "HostConfig.h" #include "Formatter.h" @@ -42,6 +43,19 @@ namespace AdaptiveCardQmlEngine std::string getLang(); void setLang(const std::string& lang); + void addHeightEstimate(const int height); + void setHeightEstimate(const int height); + const int getHeightEstimate(); + + const int getEstimatedTextHeight(const std::string text); + + const std::vector& GetWarnings(); + void AddWarning(const AdaptiveWarning& warning); + + void addToRequiredInputElementsIdList(const std::string& elementId); + const std::vector& getRequiredInputElementsIdList(); + + private: AdaptiveCardContext(); ~AdaptiveCardContext(); @@ -54,6 +68,12 @@ namespace AdaptiveCardQmlEngine std::shared_ptr mHostConfig; std::shared_ptr mCardConfig; AdaptiveCardEnums::AdaptiveCardTheme mAdaptiveCardTheme; - std::string m_lang; + std::string m_lang; + std::vector m_RequiredInputElementsIdList; + + + std::vector m_warnings; + + int m_HeightEstimate{0}; }; } diff --git a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h index 35228f05a7..998f5bc7c0 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/AdaptiveCardQmlTypes.h @@ -26,5 +26,8 @@ namespace AdaptiveCardQmlEngine qmlRegisterType(QUrl("qrc:qml/ImageRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "ImageRender"); qmlRegisterType(QUrl("qrc:qml/RichTextBlockRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "RichTextBlockRender"); qmlRegisterType(QUrl("qrc:qml/NumberInputRender.qml"), "AdaptiveCardQmlEngine", 1, 0, "NumberInputRender"); + qmlRegisterType(QUrl("qrc:qml/InputFieldClearIcon.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputFieldClearIcon"); + qmlRegisterType(QUrl("qrc:qml/InputLabel.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputLabel"); + qmlRegisterType(QUrl("qrc:qml/InputErrorMessage.qml"), "AdaptiveCardQmlEngine", 1, 0, "InputErrorMessage"); } } // namespace RendererQml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt index 3ffd9a8b7f..f4e093f5c9 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt +++ b/source/qml_v2/AdaptiveCardQmlEngine/CMakeLists.txt @@ -58,6 +58,7 @@ file(GLOB_RECURSE SOURCES "ImageModel.cpp" "RichTextBlockModel.cpp" "NumberInputModel.cpp" + "AdaptiveWarning.cpp" "AdaptiveCardQmlTypes.h" @@ -79,7 +80,8 @@ file(GLOB_RECURSE SOURCES "TextBlockModel.h" "ImageModel.h" "RichTextBlockModel.h" - "NumberInputModel.h" + "NumberInputModel.h" + "AdaptiveWarning.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/NumberInputModel.cpp b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp index 67bb7f4471..2de7b5a0b4 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.cpp @@ -7,40 +7,66 @@ NumberInputModel::NumberInputModel(std::shared_ptr numberInput, QObject* parent) : QObject(parent), mInput(numberInput) +{ + initialize(); + createInputLabel(); + createErrorMessage(); +} + +void NumberInputModel::initialize() { const auto hostConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getHostConfig(); - const auto rendererConfig = AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getCardConfig(); mVisible = mInput->GetIsVisible(); - // mPlaceholder = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetPlaceholder()) + "`"); - - createInputLabel(); + mPlaceholder = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetPlaceholder())); if (mInput->GetValue().has_value()) { mValue = mInput->GetValue().value(); - mDefaultValue = true; + mDefaultValue = "true"; } + mMinValue = mInput->GetMin().value_or(-DBL_MAX); + mMaxValue = mInput->GetMax().value_or(DBL_MAX); - mMinValue = numberInput->GetMin().value_or(-DBL_MAX); - mMaxValue = numberInput->GetMax().value_or(DBL_MAX); - - if (numberInput->GetIsRequired() || numberInput->GetMin().has_value() || numberInput->GetMax().has_value()) + if (mInput->GetIsRequired() || mInput->GetMin().has_value() || mInput->GetMax().has_value()) { + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().addToRequiredInputElementsIdList(mInput->GetId()); + mIsRequired = mInput->GetIsRequired(); + mValidationRequired = mInput->GetIsRequired(); } } + void NumberInputModel::createInputLabel() { if (!mInput->GetLabel().empty()) { - // mContext->addHeightEstimate(mContext->getEstimatedTextHeight(mInput->GetLabel())); - // mEscapedLabelString = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetLabel()) + "`"); + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().addHeightEstimate( + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().getEstimatedTextHeight(mInput->GetLabel())); + mEscapedLabelString = + QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetLabel())); + } + else + { + if (mInput->GetIsRequired()) + { + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().AddWarning(AdaptiveCardQmlEngine::AdaptiveWarning( + AdaptiveCardQmlEngine::Code::RenderException, "isRequired is not supported without labels")); + } + } +} + +void NumberInputModel::createErrorMessage() +{ + if (!mInput->GetErrorMessage().empty()) + { + // mNumberInputQmlElement->Property("_mEscapedErrorString",RendererQml::Formatter() << "String.raw`"<< RendererQml::Utils::getBackQuoteEscapedString(mInput->GetErrorMessage()) << "`"); + mEscapedErrorString = QString::fromStdString(AdaptiveCardQmlEngine::Utils::getBackQuoteEscapedString(mInput->GetErrorMessage())); } else { if (mInput->GetIsRequired()) { - // mContext->AddWarning(RendererQml::AdaptiveWarning(RendererQml::Code::RenderException, "isRequired is not supported without labels")); + AdaptiveCardQmlEngine::AdaptiveCardContext::getInstance().AddWarning(AdaptiveCardQmlEngine::AdaptiveWarning(AdaptiveCardQmlEngine::Code::RenderException, "isRequired is not supported without error message")); } } } diff --git a/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h index e77a776aad..c792c2dc4b 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/models/NumberInputModel.h @@ -14,28 +14,43 @@ class NumberInputModel : public QObject { Q_OBJECT - Q_PROPERTY(bool visible MEMBER mVisible CONSTANT); Q_PROPERTY(QString placeHolder MEMBER mPlaceholder CONSTANT); - Q_PROPERTY(QString value MEMBER mValue CONSTANT); - Q_PROPERTY(bool defaultValue MEMBER mDefaultValue CONSTANT); - Q_PROPERTY(QString minValue MEMBER mMinValue CONSTANT); - Q_PROPERTY(QString maxValue MEMBER mMaxValue CONSTANT); Q_PROPERTY(QString escapedLabelString MEMBER mEscapedLabelString CONSTANT); + Q_PROPERTY(QString defaultValue MEMBER mDefaultValue CONSTANT); + Q_PROPERTY(QString escapedErrorString MEMBER mEscapedErrorString CONSTANT); + + Q_PROPERTY(double minValue MEMBER mMinValue CONSTANT); + Q_PROPERTY(double maxValue MEMBER mMaxValue CONSTANT); + + Q_PROPERTY(bool visible MEMBER mVisible CONSTANT); + Q_PROPERTY(bool isRequired MEMBER mIsRequired CONSTANT); + Q_PROPERTY(bool validationRequired MEMBER mValidationRequired CONSTANT); + + Q_PROPERTY(int value MEMBER mValue CONSTANT); public: explicit NumberInputModel(std::shared_ptr numberInput, QObject* parent = nullptr); ~NumberInputModel(); private: + void initialize(); void createInputLabel(); - const std::shared_ptr& mInput; + void createErrorMessage(); private: - bool mVisible; + const std::shared_ptr& mInput; + QString mPlaceholder; - QString mValue; - bool mDefaultValue; - QString mMinValue; - QString mMaxValue; QString mEscapedLabelString; + QString mDefaultValue; + QString mEscapedErrorString; + + double mMinValue; + double mMaxValue; + + bool mVisible; + bool mIsRequired; + bool mValidationRequired; + + int mValue; }; 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 9649ffbbd9..6afe92a24a 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/CollectionItemDelegate.qml @@ -7,13 +7,15 @@ Loader { property var parentCardItem - Component.onCompleted :{ - if (model.delegateType == AdaptiveCardEnums.CardElementType.TextBlock) - source = "TextBlockRender.qml" - else if (model.delegateType == AdaptiveCardEnums.CardElementType.Image) - source = "ImageRender.qml" + Component.onCompleted :{ + if (model.delegateType == AdaptiveCardEnums.CardElementType.TextBlock) + source = "TextBlockRender.qml" else if (model.delegateType == AdaptiveCardEnums.CardElementType.RichTextBlock) - source: "RichTextBlockRender.qml" - } + source = "RichTextBlockRender.qml"; + else if (model.delegateType == AdaptiveCardEnums.CardElementType.Image) + source = "ImageRender.qml" + else if (model.delegateType == AdaptiveCardEnums.CardElementType.NumberInput) + source = "NumberInputRender.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..c60b42406f --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputErrorMessage.qml @@ -0,0 +1,48 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.15 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils + +Rectangle { + id: 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: numberinputModel.escapedErrorString + } +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml new file mode 100644 index 0000000000..6eebe2e0d7 --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputFieldClearIcon.qml @@ -0,0 +1,31 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.15 +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..889647692b --- /dev/null +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/InputLabel.qml @@ -0,0 +1,16 @@ +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.15 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils +Label { + id: inputLabel + + wrapMode: Text.Wrap + width: parent.width + color: cardConst.toggleButtonConstants.textColor + font.pixelSize: cardConst.inputFieldConstants.labelPixelSize + Accessible.ignored: true + text: numberinputModel.isRequired ? AdaptiveCardUtils.escapeHtml( numberinputModel.escapedLabelString) + " " + "*" : AdaptiveCardUtils.escapeHtml( numberinputModel.escapedLabelString) +} diff --git a/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml b/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml index 0f4bf05c7c..b5f3b6cce2 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml +++ b/source/qml_v2/AdaptiveCardQmlEngine/qml/NumberInputRender.qml @@ -1,12 +1,280 @@ -import QtQuick 2.3 - -Rectangle { - width: 200 - height: 100 - color: "red" +import QtQuick 2.15 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.15 +import QtQuick.Controls 2.15 +import AdaptiveCardQmlEngine 1.0 +import "JSUtils/AdaptiveCardUtils.js" as AdaptiveCardUtils +Column { + id: numberInput + property var numberinputModel:model.numberInputRole + property bool showErrorMessage: false + property int spinBoxMinVal : Math.max(-2147483648, numberinputModel.minValue) + property int spinBoxMaxVal : Math.min(2147483647, numberinputModel.maxValue) + CardConstants{ + id:cardConst + + } - Text { - anchors.centerIn: parent - text: "Hello, World!" + function validate() { + if (numberInputTextField.text.length !== 0 && Number(numberInputTextField.text) >= numberinputModel.minValue && Number(numberInputTextField.text) <= numberinputModel.maxValue) { + showErrorMessage = false; + return false; + } else { + return true; + } + } + + function getAccessibleName() { + let accessibleName = ''; + if (showErrorMessage) + accessibleName += 'Error. ' + numberinputModel.escapedErrorString + '. '; + + if (numberinputModel.escapedLabelString) + accessibleName += numberinputModel.escapedLabelString + '. '; + + if (numberInputTextField.text !== '') + accessibleName += (numberInputTextField.text); + else + accessibleName += numberinputModel.placeHolder; + accessibleName += qsTr(", Type the number"); + return accessibleName; + } + width: parent.width + spacing: cardConst.inputFieldConstants.columnSpacing + onShowErrorMessageChanged: { + numberInputRectangle.colorChange(false); + } + onActiveFocusChanged: { + if (activeFocus) + numberInputTextField.forceActiveFocus(); + + } + visible: numberinputModel.visible + InputLabel { + id: numberInputLabel + visible:numberinputModel.escapedLabelString + } + + Row { + id: numberInputRow + + width: parent.width + height: cardConst.inputFieldConstants.height + + Rectangle { + id: numberInputRectangle + + function colorChange(isPressed) { + if (isPressed && !showErrorMessage) + color = inputFieldConstants.backgroundColorOnPressed; + else + color = numberInputTextField.activeFocus ? cardConst.inputFieldConstants.backgroundColorOnPressed : numberInputTextField.hovered ? cardConst.inputFieldConstants.backgroundColorOnHovered : cardConst.inputFieldConstants.backgroundColorNormal; + } + + border.width: cardConst.inputFieldConstants.borderWidth + border.color: showErrorMessage ? cardConst.inputFieldConstants.borderColorOnError : cardConst.inputFieldConstants.borderColorNormal + radius: cardConst.inputFieldConstants.borderRadius + height: parent.height + color: numberInputSpinBox.Opressed ? cardConst.inputFieldConstants.backgroundColorOnPressed : numberInputSpinBox.hovered ? cardConst.inputFieldConstants.backgroundColorOnHovered : cardConst.inputFieldConstants.backgroundColorNormal + width: parent.width - numberInputArrowRectangle.width + WCustomFocusItem { + isRectangle: true + visible: numberInputTextField.activeFocus + } + + SpinBox { + id: numberInputSpinBox + + + function changeValue(keyPressed) { + if ((keyPressed === Qt.Key_Up || keyPressed === Qt.Key_Down) && numberInputTextField.text.length === 0) { + value = (from > 0) ? from : 0; + } else if (keyPressed === Qt.Key_Up) { + numberInputSpinBox.value = Number(numberInputTextField.text); + numberInputSpinBox.increase(); + } else if (keyPressed === Qt.Key_Down) { + numberInputSpinBox.value = Number(numberInputTextField.text); + numberInputSpinBox.decrease(); + } + numberInputTextField.text = numberInputSpinBox.value; + } + + width: parent.width - numberInputClearIcon.width - cardConst.inputFieldConstants.clearIconHorizontalPadding + padding: 0 + editable: true + stepSize: 1 + to: spinBoxMaxVal + from: spinBoxMinVal + Keys.onPressed: { + if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) { + numberInputSpinBox.changeValue(event.key); + event.accepted = true; + } + } + Accessible.ignored: true + Component.onCompleted: { + if (numberinputModel.defaultValue) + numberInputSpinBox.value = numberinputModel.value; + + } + + contentItem: TextField { + id: numberInputTextField + + font.pixelSize: cardConst.inputFieldConstants.pixelSize + anchors.left: parent.left + anchors.right: parent.right + selectByMouse: true + selectedTextColor: 'white' + readOnly: !numberInputSpinBox.editable + validator: numberInputSpinBox.validator + inputMethodHints: Qt.ImhFormattedNumbersOnly + onPressed: { + numberInputRectangle.colorChange(true); + event.accepted = true; + } + onReleased: { + numberInputRectangle.colorChange(false); + forceActiveFocus(); + event.accepted = true; + } + onHoveredChanged: numberInputRectangle.colorChange(false) + onActiveFocusChanged: { + numberInputRectangle.colorChange(false); + Accessible.name = getAccessibleName(); + } + + leftPadding: cardConst.inputFieldConstants.textHorizontalPadding + rightPadding: cardConst.inputFieldConstants.textHorizontalPadding + topPadding: cardConst.inputFieldConstants.textVerticalPadding + bottomPadding: cardConst.inputFieldConstants.textVerticalPadding + placeholderText: numberinputModel.placeHolder + Accessible.role: Accessible.EditableText + color: cardConst.inputFieldConstants.textColor + placeholderTextColor: cardConst.inputFieldConstants.placeHolderColor + onTextChanged: { + validate(); + } + Component.onCompleted: { + if (numberinputModel.defaultValue) + numberInputTextField.text = numberInputSpinBox.value; + } + + background: Rectangle { + id: numberInputTextFieldBg + + color: 'transparent' + } + + } + + background: Rectangle { + id: numberSpinBoxBg + + color: 'transparent' + } + + up.indicator: Rectangle { + color: 'transparent' + z: -1 + } + + down.indicator: Rectangle { + color: 'transparent' + z: -1 + } + + validator: DoubleValidator { + } + } + + InputFieldClearIcon { + id: numberInputClearIcon + + Keys.onReturnPressed: onClicked() + visible: numberInputTextField.length !== 0 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: cardConst.inputFieldConstants.clearIconHorizontalPadding + onClicked: { + nextItemInFocusChain().forceActiveFocus(); + numberInputSpinBox.value = numberInputSpinBox.from; + numberInputTextField.clear(); + } + } + } + + Rectangle { + id: numberInputArrowRectangle + + property string accessibilityPrefix: '' + + width: cardConst.inputNumberConstants.upDownButtonWidth + radius: cardConst.inputFieldConstants.borderRadius + height: parent.height + border.color: cardConst.inputFieldConstants.borderColorNormal + activeFocusOnTab: true + color:(numberInputArrowIcon.pressed || activeFocus) ? cardConst.inputFieldConstants.backgroundColorOnPressed : numberInputArrowIcon.hovered ? cardConst.inputFieldConstants.backgroundColorOnHovered : cardConst.inputFieldConstants.backgroundColorNormal + + onActiveFocusChanged: { + if (activeFocus) + accessibilityPrefix = qsTr("Use up arrow to increase the value and down arrow to decrease the value") + (numberInputTextField.text ? ", Current number is " : ""); + + } + Accessible.name: accessibilityPrefix + numberInputTextField.displayText + Accessible.role: Accessible.NoRole + + Button { + id: numberInputArrowIcon + + width: parent.width + anchors.right: parent.right + horizontalPadding: cardConst.inputFieldConstants.iconPadding + verticalPadding: cardConst.inputFieldConstants.iconPadding + icon.width: cardConst.numberInputConstants.upDownIconSize + icon.height: cardConst.numberInputConstants.upDownIconSize + focusPolicy: Qt.NoFocus + icon.color: cardConst.inputNumberConstants.upDownIconColor + height: parent.height + icon.source: cardConst.numberInputUpDownArrowImage + + background: Rectangle { + color: 'transparent' + } + } + + MouseArea { + id: numberInputSpinBoxUpIndicatorArea + + width: parent.width + height: parent.height / 2 + anchors.top: parent.top + onReleased: { + numberInputSpinBox.changeValue(Qt.Key_Up); + numberInputArrowRectangle.forceActiveFocus(); + } + } + + MouseArea { + id: numberInputSpinBoxDownIndicatorArea + + width: parent.width + height: parent.height / 2 + anchors.top: numberInputSpinBoxUpIndicatorArea.bottom + onReleased: { + numberInputSpinBox.changeValue(Qt.Key_Down); + numberInputArrowRectangle.forceActiveFocus(); + } + } + + WCustomFocusItem { + isRectangle: true + } + } + } + + InputErrorMessage { + id: numberInputErrorMessage + 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 29381eb22d..4cfeb92e46 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc +++ b/source/qml_v2/AdaptiveCardQmlEngine/resourceEngine.qrc @@ -17,5 +17,8 @@ images/4.jpg qml/RichTextBlockRender.qml qml/NumberInputRender.qml + qml/InputLabel.qml + qml/InputFieldClearIcon.qml + qml/InputErrorMessage.qml diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp index 4400f3373c..f393cf20a7 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.cpp @@ -208,6 +208,23 @@ 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; + } + } // namespace AdaptiveCardQmlEngine diff --git a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h index d9a3ecd835..32c72315a1 100644 --- a/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h +++ b/source/qml_v2/AdaptiveCardQmlEngine/utils/Utils.h @@ -17,6 +17,7 @@ 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); template static bool IsInstanceOfSmart(U u); diff --git a/source/qml_v2/AdaptiveCardVisualizer/JSONSamples/numberInput.json b/source/qml_v2/AdaptiveCardVisualizer/JSONSamples/numberInput.json new file mode 100644 index 0000000000..0286aca842 --- /dev/null +++ b/source/qml_v2/AdaptiveCardVisualizer/JSONSamples/numberInput.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "TextBlock", + "text": "Default text input" + }, + { + "type": "Input.Text", + "id": "defaultInputId", + "placeholder": "enter comment", + "maxLength": 500 + }, + { + "type": "TextBlock", + "text": "Multiline text input" + }, + { + "type": "Input.Text", + "id": "multilineInputId", + "placeholder": "enter comment", + "maxLength": 50, + "isMultiline": true + }, + { + "type": "TextBlock", + "text": "Input Number" + }, + { + "type": "Input.Number", + "id": "number", + "placeholder": "Enter a number", + "min": 1, + "max": 10, + "value": 3 + } + ], + "actions": [ + { + "type": "Action.Submit", + "title": "OK" + } + ] +} diff --git a/source/qml_v2/AdaptiveCardVisualizer/SampleCardListModel.cpp b/source/qml_v2/AdaptiveCardVisualizer/SampleCardListModel.cpp index 392880625b..0b5d2a7645 100644 --- a/source/qml_v2/AdaptiveCardVisualizer/SampleCardListModel.cpp +++ b/source/qml_v2/AdaptiveCardVisualizer/SampleCardListModel.cpp @@ -56,8 +56,8 @@ void SampleCardListModel::populateCardDisplayNames() mCardDisplayNames.insert("textBlock.json", "TextBlock"); mCardDisplayNames.insert("richTextBlock.json", "Rich text"); mCardDisplayNames.insert("inputText.json", "Input text"); + mCardDisplayNames.insert("numberInput.json", "Input number"); mCardDisplayNames.insert("actionInline.json", "Action Inline"); - mCardDisplayNames.insert("inputNumber.json", "Input number"); mCardDisplayNames.insert("dateInput.json", "Input date"); mCardDisplayNames.insert("checkBoxInput.json", "Input Toggle"); mCardDisplayNames.insert("choiceSetInput.json", "Input ChoiceSet");