diff --git a/ViewSolutions/ViewSolutions/Extendable.qml b/ViewSolutions/ViewSolutions/Extendable.qml new file mode 100644 index 0000000..d6c90d4 --- /dev/null +++ b/ViewSolutions/ViewSolutions/Extendable.qml @@ -0,0 +1,83 @@ +//# +//# Copyright (C) 2025-2025 QuasarApp. +//# Distributed under the GPLv3 software license, see the accompanying +//# Everyone is permitted to copy and distribute verbatim copies +//# of this license document, but changing it is not allowed. +//# + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Control { + + id: root + + property alias extendableWidget: extendetArea.contentItem + property alias mainWidget: mainButton.contentItem + // see GridLayout + property alias layoutDirection: columnLayout.layoutDirection + property alias flow: columnLayout.flow + + property alias extended: extendetArea.visible + property int animationDuration: 600 + + contentItem: GridLayout { + id: columnLayout + columnSpacing: 0 + rowSpacing: 0 + + Control { + Layout.alignment: Qt.AlignCenter + Layout.maximumWidth: root.implicitWidth - root.rightPadding - root.leftPadding + padding: 0 + id: mainButton + + + Behavior on implicitHeight { + enabled: root.flow !== GridLayout.LeftToRight + + NumberAnimation { + easing.type: Easing.OutExpo + duration: root.animationDuration + } + } + + Behavior on implicitWidth { + enabled: root.flow === GridLayout.LeftToRight + NumberAnimation { + easing.type: Easing.OutExpo + duration: root.animationDuration + } + } + } + + + Control { + id: extendetArea + clip: true + visible: false + padding: 0 + Layout.alignment: Qt.AlignCenter + Layout.maximumWidth: root.implicitWidth - root.rightPadding - root.leftPadding + + Behavior on implicitHeight { + enabled: root.flow !== GridLayout.LeftToRight + + NumberAnimation { + easing.type: Easing.OutExpo + duration: root.animationDuration + } + } + + Behavior on implicitWidth { + enabled: root.flow === GridLayout.LeftToRight + NumberAnimation { + easing.type: Easing.OutExpo + duration: root.animationDuration + } + } + + } + } +} diff --git a/ViewSolutions/ViewSolutions/ImageView.qml b/ViewSolutions/ViewSolutions/ImageView.qml index 0c01aec..6b99a23 100644 --- a/ViewSolutions/ViewSolutions/ImageView.qml +++ b/ViewSolutions/ViewSolutions/ImageView.qml @@ -16,6 +16,7 @@ AbstractButton { id: root property string source: "" property alias imagesource: sourceImg + property alias imagEffect: imgEffect property int radius: 16 property real power: 1.0 @@ -121,6 +122,7 @@ AbstractButton { source: Image { id: sourceImg source: root.source + mipmap: true clip: true fillMode: Image.PreserveAspectCrop diff --git a/ViewSolutions/ViewSolutions/Metrix.qml b/ViewSolutions/ViewSolutions/Metrix.qml deleted file mode 100644 index b3d2adc..0000000 --- a/ViewSolutions/ViewSolutions/Metrix.qml +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2018-2024 QuasarApp. - * Distributed under the GPLv3 software license, see the accompanying - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. -*/ - -import QtQuick -import QtQuick.Window -import QtQuick.Controls.Material -import QtQuick.Controls - -Item { - readonly property int pointCount: 100; - readonly property real mm: Screen.pixelDensity - readonly property real sm: 10 * mm - readonly property real dsm: Math.sqrt(Math.pow(Screen.desktopAvailableWidth, 2) + Math.pow(Screen.desktopAvailableHeight, 2)) / sm - readonly property real pt: getfactor(dsm) * sm - readonly property real controlPtMaterial: Material.buttonHeight - readonly property real gamePt: (width < height) ? width / pointCount : height / pointCount; - - function getfactor(dsm) { - if ( dsm < 70) { - return 1 - } else if (dsm < 140) { - return 2; - } else - return 4; - } - - anchors.fill: parent; -} diff --git a/ViewSolutions/ViewSolutions/NotificationServiceView.qml b/ViewSolutions/ViewSolutions/NotificationServiceView.qml index 1fc37a4..b0f9885 100644 --- a/ViewSolutions/ViewSolutions/NotificationServiceView.qml +++ b/ViewSolutions/ViewSolutions/NotificationServiceView.qml @@ -18,45 +18,41 @@ Item { readonly property var history: model.history - Metrix { - id: metrix - } - NotificationForm { id: notyfyView - titleText : msg.title(); - text: (msg)? msg.text(): ""; - img: (msg && msg.img().length)? msg.img(): getDefaultImage((msg)? msg.type(): 0); - type: (msg)? msg.type(): 0; + titleText : root.msg.title(); + text: (root.msg)? root.msg.text(): ""; + img: (root.msg && root.msg.img().length)? root.msg.img(): getDefaultImage((root.msg)? root.msg.type(): 0); + type: (root.msg)? root.msg.type(): 0; x: parent.width - width - margin; y: margin; - width: Math.min(6 * metrix.pt, root.width); + width: Math.min(440, root.width); } YesNoQuestion { id: questionMsgBox - titleText : qst.title(); - text: (qst)? qst.text(): ""; - img: (qst && qst.img().length)? qst.img(): defImg; + titleText : root.qst.title(); + text: (root.qst)? root.qst.text(): ""; + img: (root.qst && root.qst.img().length)? root.qst.img(): defImg; type: 0; x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2; - width: Math.min(6 * metrix.pt, root.width); + width: Math.min(440, root.width); onAccepted: { - if (model) { - model.questionComplete(true, qst.type()) + if (root.model) { + root.model.questionComplete(true, root.qst.type()) } } onRejected: { - if (model) { - model.questionComplete(false, qst.type()) + if (root.model) { + root.model.questionComplete(false, root.qst.type()) } } } @@ -83,7 +79,7 @@ Item { } Connections { - target: model + target: root.model function onSigShowHistory() { history.open() } diff --git a/ViewSolutions/ViewSolutions/StackText.qml b/ViewSolutions/ViewSolutions/StackText.qml new file mode 100644 index 0000000..e75fabb --- /dev/null +++ b/ViewSolutions/ViewSolutions/StackText.qml @@ -0,0 +1,88 @@ +//# +//# Copyright (C) 2025-2025 QuasarApp. +//# Distributed under the GPLv3 software license, see the accompanying +//# Everyone is permitted to copy and distribute verbatim copies +//# of this license document, but changing it is not allowed. +//# + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import ViewSolutions +import QtQuick.Effects + +Control { + id: root + padding: 24 + + required property var guiTokens + property alias text: model.fullText; + property alias delimiter: model.delimiter + property alias delegate: contentList.delegate + readonly property bool isFinished: contentList.currentIndex >= contentList.count - 1 + + function next() { + if (nextButton.visible) { + nextButton.click() + } + } + + contentItem: ColumnLayout { + ListView { + id: contentList + currentIndex: 0 + interactive: false + // snapMode: ListView.SnapOneItem + boundsBehavior:Flickable.StopAtBounds + Layout.fillWidth: true + Layout.fillHeight: true + + model: StackTextModel { + id: model; + + onFullTextChanged: { + contentList.currentIndex = 0; + } + } + } + + ToolButton { + Layout.alignment: Qt.AlignHCenter + + + id: nextButton + text: qsTr("Next") + visible: contentList.count > 1 && contentList.currentIndex < contentList.count - 1 + opacity: visible + + Behavior on opacity { + NumberAnimation { + easing.type: Easing.InOutQuad + duration: 300 + } + } + + font: root.font + + onClicked: { + if (contentList.currentIndex + 1 >= contentList.count) { + contentList.currentIndex = 0; + return; + } + contentList.currentIndex = contentList.currentIndex + 1; + } + + layer.enabled: true + layer.effect: MultiEffect { + shadowBlur: 1.0 + shadowEnabled: true + shadowColor: root.guiTokens.color_accent_primary + shadowScale: 1.0 + } + + } + } + +} diff --git a/ViewSolutions/src/basehashmodel.h b/ViewSolutions/src/basehashmodel.h index 9f7cdd8..432c86e 100644 --- a/ViewSolutions/src/basehashmodel.h +++ b/ViewSolutions/src/basehashmodel.h @@ -43,11 +43,12 @@ class BaseHashModel: public QAbstractListModel { public: + BaseHashModel(QObject* parent = nullptr): QAbstractListModel(parent) { } - int rowCount(const QModelIndex &parent) const override { + int rowCount(const QModelIndex &) const override { return m_data.size(); } @@ -114,6 +115,10 @@ class BaseHashModel: public QAbstractListModel return {}; } + DATA get(const KEY& key) { + return m_data.value(key); + } + const QHash& dateList() const { return m_data; } diff --git a/ViewSolutions/src/notificationdata.h b/ViewSolutions/src/notificationdata.h index 7b2f07b..3fa5070 100644 --- a/ViewSolutions/src/notificationdata.h +++ b/ViewSolutions/src/notificationdata.h @@ -26,11 +26,14 @@ class VIEWSOLUTION_EXPORT NotificationData */ enum Type { /// This is message for general notification. - Normal, + Normal = 0, /// This is warning notification. Warning = 1, /// This is critical error notifications. Error = 2, + + /// This is user defined type of message. + Custom = 3, }; explicit NotificationData(const QString& title = "", diff --git a/ViewSolutions/src/notificationservice.cpp b/ViewSolutions/src/notificationservice.cpp index 29fe707..fb19932 100644 --- a/ViewSolutions/src/notificationservice.cpp +++ b/ViewSolutions/src/notificationservice.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2024 QuasarApp. + * Copyright (C) 2018-2025 QuasarApp. * Distributed under the GPLv3 software license, see the accompanying * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. @@ -117,14 +117,41 @@ void NotificationService::showHistory() { emit sigShowHistory(); } +void NotificationService::notificationHiden() { + setUiIsDisplay(false); +} + int NotificationService::notificationsCount() const { return _history->rowCount({}); } - QString ViewSolutions::NotificationService::modelId() const { return "NotificationService"; } +bool NotificationService::uiIsDisplay() const { + return _uiIsDisplay && time(0) - _lastDisplayTime < _uiTimeOut; +} + +void NotificationService::setUiIsDisplay(bool newUiIsDisplay) { + if (_uiIsDisplay == newUiIsDisplay) + return; + + if (_uiIsDisplay) { + _lastDisplayTime = time(0); + } + + _uiIsDisplay = newUiIsDisplay; + emit uiIsDisplayChanged(); +} + +int NotificationService::uiTimeOut() const { + return _uiTimeOut; +} + +void NotificationService::setUiTimeOut(int newUiTimeOut) { + _uiTimeOut = newUiTimeOut; +} + } diff --git a/ViewSolutions/src/notificationservice.h b/ViewSolutions/src/notificationservice.h index d954141..d9cf892 100644 --- a/ViewSolutions/src/notificationservice.h +++ b/ViewSolutions/src/notificationservice.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2024 QuasarApp. + * Copyright (C) 2018-2025 QuasarApp. * Distributed under the GPLv3 software license, see the accompanying * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. @@ -32,12 +32,15 @@ class VIEWSOLUTION_EXPORT NotificationService: public QObject, public iModel Q_PROPERTY(NotificationData notify READ notify NOTIFY notifyChanged) Q_PROPERTY(NotificationData question READ question NOTIFY questionChanged) + Q_PROPERTY(bool uiIsDisplay READ uiIsDisplay WRITE setUiIsDisplay NOTIFY uiIsDisplayChanged FINAL) Q_PROPERTY(QObject* history READ history NOTIFY notifyChanged) Q_PROPERTY(int notificationsCount READ notificationsCount NOTIFY countNotificationsChanged) public: + explicit NotificationService(QObject *ptr = nullptr); + /** * @brief Notify This method return data of the last notify message. * @return return data of the last notify message. @@ -131,6 +134,11 @@ class VIEWSOLUTION_EXPORT NotificationService: public QObject, public iModel Q_INVOKABLE void showHistory(); + /** + * @brief notificationHiden this method should invoked every time when the current notification is hiden, automaticaly or by user. + */ + Q_INVOKABLE void notificationHiden(); + /** * @brief notificationsCount - This method used for return count of history notifications. * @return count of history notifications. @@ -143,6 +151,16 @@ class VIEWSOLUTION_EXPORT NotificationService: public QObject, public iModel public: QString modelId() const override; + bool uiIsDisplay() const; + void setUiIsDisplay(bool newUiIsDisplay); + + /** + * @brief uiTimeOut this is maximu time that notify service can be loked fo display. + * @return + */ + int uiTimeOut() const; + void setUiTimeOut(int newUiTimeOut); + signals: /** * @brief notifyChanged This signal emited whet the notificator (Ths object) received a new notification message. @@ -168,14 +186,19 @@ class VIEWSOLUTION_EXPORT NotificationService: public QObject, public iModel void countNotificationsChanged(); + void uiIsDisplayChanged(); + private: - explicit NotificationService(QObject *ptr = nullptr); QHash _listners; NotificationData _question; NotificationData _notify; + bool _uiIsDisplay = false; + int _uiTimeOut = 60; + int _lastDisplayTime = 0; + HistoryNotificationsModel* _history = nullptr; diff --git a/ViewSolutions/src/stacktextmodel.cpp b/ViewSolutions/src/stacktextmodel.cpp new file mode 100644 index 0000000..65e2cd5 --- /dev/null +++ b/ViewSolutions/src/stacktextmodel.cpp @@ -0,0 +1,66 @@ +//# +//# Copyright (C) 2025-2025 QuasarApp. +//# Distributed under the GPLv3 software license, see the accompanying +//# Everyone is permitted to copy and distribute verbatim copies +//# of this license document, but changing it is not allowed. +//# + +#include "stacktextmodel.h" + +StackTextModel::StackTextModel() {} + +QString StackTextModel::fullText() const { + return _fullText; +} + +void StackTextModel::setFullText(const QString &newFullText) { + if (_fullText == newFullText) + return; + _fullText = newFullText; + updateText(); + emit fullTextChanged(); +} + +QString StackTextModel::delimiter() const { + return _delimiter.pattern(); +} + +void StackTextModel::setDelimiter(const QString &newDelimiter) { + if (_delimiter.pattern() == newDelimiter) + return; + _delimiter.setPattern(newDelimiter); + updateText(); + emit delimiterChanged(); +} + +void StackTextModel::updateText() { + QStringList lines; + QString line; + + int delimeterIdx = _fullText.indexOf(_delimiter); + int lastIdx = -1; + while (delimeterIdx >= 0) { + line = _fullText.mid(lastIdx + 1, delimeterIdx - lastIdx); + lastIdx = delimeterIdx; + delimeterIdx = _fullText.indexOf(_delimiter, lastIdx + 1); + + if (line.size()) { + lines += line; + } + + } + + // take last string. + line = _fullText.mid(lastIdx + 1, delimeterIdx); + if (line.size()) { + lines += line; + } + + setStringList(lines); +} + +QHash StackTextModel::roleNames() const { + QHash roles; + roles[Qt::DisplayRole] = "display"; + return roles; +} diff --git a/ViewSolutions/src/stacktextmodel.h b/ViewSolutions/src/stacktextmodel.h new file mode 100644 index 0000000..ffca14d --- /dev/null +++ b/ViewSolutions/src/stacktextmodel.h @@ -0,0 +1,73 @@ +//# +//# Copyright (C) 2025-2025 QuasarApp. +//# Distributed under the GPLv3 software license, see the accompanying +//# Everyone is permitted to copy and distribute verbatim copies +//# of this license document, but changing it is not allowed. +//# + + +#ifndef STACKTEXTMODEL_H +#define STACKTEXTMODEL_H + +#include +#include +#include "viewsolutions_global.h" + +/** + * @brief The StackTextModel class is model that contains stack text lines. + * This model split full text into lines by delimiter. + */ +class VIEWSOLUTION_EXPORT StackTextModel: public QStringListModel +{ + Q_OBJECT + + Q_PROPERTY(QString fullText READ fullText WRITE setFullText NOTIFY fullTextChanged FINAL) + Q_PROPERTY(QString delimiter READ delimiter WRITE setDelimiter NOTIFY delimiterChanged FINAL) + +public: + StackTextModel(); + + // QAbstractItemModel interface + + /** + * @brief fullText returns full text from all lines. + * @return full text from all lines. + */ + QString fullText() const; + void setFullText(const QString &newFullText); + + /** + * @brief delimiter returns delimiter that used to split fullText into lines. + * @return delimiter that used to split fullText into lines. + * @default is "\n" + */ + QString delimiter() const; + + /** + * @brief setDelimiter sets new delimiter for split fullText into lines. + * @param newDelimiter new delimiter for split fullText into lines. + */ + void setDelimiter(const QString &newDelimiter); + QHash roleNames() const override; + +protected: + + /** + * @brief updateText updates internal model data from fullText and delimiter. + */ + virtual void updateText(); + +signals: + void fullTextChanged(); + void delimiterChanged(); + +private: + + QString _fullText; + QRegularExpression _delimiter = QRegularExpression("([.?!;]+)"); + + // QAbstractItemModel interface +public: +}; + +#endif // STACKTEXTMODEL_H diff --git a/ViewSolutions/src/viewsolutions.cpp b/ViewSolutions/src/viewsolutions.cpp index 3eb58c8..0173615 100644 --- a/ViewSolutions/src/viewsolutions.cpp +++ b/ViewSolutions/src/viewsolutions.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace ViewSolutions { QSharedPointer init(QQmlEngine *engine) { @@ -37,7 +38,9 @@ QSharedPointer init(QQmlEngine *engine) { // to-do - remove root->setContextProperty("colorPicker", picker.get()); - qRegisterMetaType("VariantListModel"); + qmlRegisterType("ViewSolutions", 1, 0, "VariantListModel"); + qmlRegisterType("ViewSolutions", 1, 0, "StackTextModel"); + return storage; }