From 52ac17864bee8cbb99e754adfffae97ba18d7c73 Mon Sep 17 00:00:00 2001
From: Skycoder42
Date: Sun, 18 Feb 2018 21:17:56 +0100
Subject: [PATCH] added basic message box
---
src/imports/mvvmcore/plugins.qmltypes | 78 +++++++++++
src/imports/mvvmcore/qtmvvmcore_plugin.cpp | 7 +-
src/imports/mvvmquick/DialogPresenter.qml | 32 +++++
src/imports/mvvmquick/MsgBox.qml | 128 +++++++++++++++++++
src/imports/mvvmquick/QtMvvmApp.qml | 15 ++-
src/imports/mvvmquick/TintIcon.qml | 39 ++++++
src/imports/mvvmquick/mvvmquick.pro | 5 +-
src/imports/mvvmquick/qmldir | 4 +
src/imports/mvvmquick/qqmlquickpresenter.cpp | 27 +++-
src/imports/mvvmquick/qqmlquickpresenter.h | 2 +
src/mvvmcore/message.cpp | 16 +++
src/mvvmcore/message.h | 5 +-
src/mvvmquick/quickpresenter.cpp | 7 +-
13 files changed, 354 insertions(+), 11 deletions(-)
create mode 100644 src/imports/mvvmquick/DialogPresenter.qml
create mode 100644 src/imports/mvvmquick/MsgBox.qml
create mode 100644 src/imports/mvvmquick/TintIcon.qml
diff --git a/src/imports/mvvmcore/plugins.qmltypes b/src/imports/mvvmcore/plugins.qmltypes
index aef4d35..75edfad 100644
--- a/src/imports/mvvmcore/plugins.qmltypes
+++ b/src/imports/mvvmcore/plugins.qmltypes
@@ -8,6 +8,84 @@ import QtQuick.tooling 1.2
Module {
dependencies: ["QtQuick 2.8"]
+ Component {
+ name: "QtMvvm::MessageConfig"
+ exports: ["de.skycoder42.QtMvvm.Core/MessageConfig 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "StandardButtons"
+ values: {
+ "NoButton": 0,
+ "Ok": 1024,
+ "Save": 2048,
+ "SaveAll": 4096,
+ "Open": 8192,
+ "Yes": 16384,
+ "YesToAll": 32768,
+ "No": 65536,
+ "NoToAll": 131072,
+ "Abort": 262144,
+ "Retry": 524288,
+ "Ignore": 1048576,
+ "Close": 2097152,
+ "Cancel": 4194304,
+ "Discard": 8388608,
+ "Help": 16777216,
+ "Apply": 33554432,
+ "Reset": 67108864,
+ "RestoreDefaults": 134217728
+ }
+ }
+ Property { name: "type"; type: "QByteArray" }
+ Property { name: "subType"; type: "QByteArray" }
+ Property { name: "title"; type: "string" }
+ Property { name: "text"; type: "string" }
+ Property { name: "buttons"; type: "StandardButtons" }
+ Property { name: "buttonTexts"; type: "QVariantMap" }
+ Property { name: "defaultValue"; type: "QVariant" }
+ Property { name: "viewProperties"; type: "QVariantMap" }
+ }
+ Component {
+ name: "QtMvvm::MessageResult"
+ prototype: "QObject"
+ exports: ["de.skycoder42.QtMvvm.Core/MessageResult 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Property { name: "result"; type: "QVariant" }
+ Property { name: "autoDelete"; type: "bool" }
+ Signal {
+ name: "dialogDone"
+ Parameter { name: "result"; type: "QtMvvm::MessageConfig::StandardButton" }
+ }
+ Signal {
+ name: "autoDeleteChanged"
+ Parameter { name: "autoDelete"; type: "bool" }
+ }
+ Method { name: "discardMessage" }
+ Method {
+ name: "setResult"
+ Parameter { name: "result"; type: "QVariant" }
+ }
+ Method {
+ name: "setAutoDelete"
+ Parameter { name: "autoDelete"; type: "bool" }
+ }
+ Method {
+ name: "setCloseTarget"
+ Parameter { name: "closeObject"; type: "QObject"; isPointer: true }
+ Parameter { name: "closeMethod"; type: "QMetaMethod" }
+ }
+ Method {
+ name: "complete"
+ Parameter { name: "result"; type: "QtMvvm::MessageConfig::StandardButton" }
+ }
+ Method {
+ name: "complete"
+ Parameter { name: "result"; type: "QtMvvm::MessageConfig::StandardButton" }
+ Parameter { name: "resultValue"; type: "QVariant" }
+ }
+ }
Component {
name: "QtMvvm::QQmlMvvmBinding"
prototype: "QObject"
diff --git a/src/imports/mvvmcore/qtmvvmcore_plugin.cpp b/src/imports/mvvmcore/qtmvvmcore_plugin.cpp
index 68b2fbf..7708762 100644
--- a/src/imports/mvvmcore/qtmvvmcore_plugin.cpp
+++ b/src/imports/mvvmcore/qtmvvmcore_plugin.cpp
@@ -3,6 +3,7 @@
#include
#include
+#include
#include "qqmlmvvmbinding.h"
#include "qqmlmvvmmessage.h"
@@ -21,8 +22,12 @@ void QtMvvmCoreDeclarativeModule::registerTypes(const char *uri)
Q_ASSERT(qstrcmp(uri, "de.skycoder42.QtMvvm.Core") == 0);
//Version 1.0
- qmlRegisterUncreatableType(uri, 1, 0, "ViewModel", tr("ViewModels cannot be created from QML"));
+ qmlRegisterUncreatableType(uri, 1, 0, "MessageConfig", QStringLiteral("Q_GADGETS cannot be created from QML"));
+ qmlRegisterUncreatableType(uri, 1, 0, "MessageResult", QStringLiteral("Message results must be passed from C++"));
+ qmlRegisterUncreatableType(uri, 1, 0, "ViewModel", QStringLiteral("ViewModels cannot be created from QML"));
+
qmlRegisterType(uri, 1, 0, "MvvmBinding");
+
qmlRegisterSingletonType(uri, 1, 0, "Message", createMessageSingleton);
// Check to make shure no module update is forgotten
diff --git a/src/imports/mvvmquick/DialogPresenter.qml b/src/imports/mvvmquick/DialogPresenter.qml
new file mode 100644
index 0000000..74c7f5e
--- /dev/null
+++ b/src/imports/mvvmquick/DialogPresenter.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.10
+import QtQuick.Controls 2.3
+import de.skycoder42.QtMvvm.Core 1.0
+
+QtObject {
+ id: _dialogPresenter
+
+ property Item rootItem: null
+
+ function showDialog(config, result) {
+ if(config.type == "msgbox") {
+ createMsgBox(config, result)
+ return true;
+ } else
+ return false;
+ }
+
+ property Component _msgBoxComponent: Component {
+ MsgBox {
+ id: __msgBox
+ Component.onCompleted: __msgBox.open()
+ }
+ }
+
+ function createMsgBox(config, result) {
+ var props = config.viewProperties;
+ props["msgConfig"] = config;
+ props["msgResult"] = result;
+ var incubator = _msgBoxComponent.incubateObject(rootItem, props);
+ return incubator.status !== Component.Error;
+ }
+}
diff --git a/src/imports/mvvmquick/MsgBox.qml b/src/imports/mvvmquick/MsgBox.qml
new file mode 100644
index 0000000..55d9d85
--- /dev/null
+++ b/src/imports/mvvmquick/MsgBox.qml
@@ -0,0 +1,128 @@
+import QtQuick 2.10
+import QtQuick.Controls 2.3
+import QtQuick.Window 2.2
+import QtQuick.Layouts 1.3
+import de.skycoder42.QtMvvm.Core 1.0
+
+Dialog {
+ id: _msgBox
+
+ property var msgConfig
+ property MessageResult msgResult
+
+ property real extraHeight: 0
+
+ x: parent ? (parent.width - width) / 2 : 0
+ y: parent ? deltaY() : 0
+ width: parent ? Math.min(Math.max(implicitWidth, 300), parent.width - 28) : implicitWidth
+ height: parent ? Math.min(implicitHeight, parent.height - 28) : implicitWidth
+ modal: true
+ focus: true
+ closePolicy: Popup.CloseOnEscape
+
+ function deltaY() {
+ var unscaled = Qt.inputMethod.keyboardRectangle.height / Screen.devicePixelRatio;
+ var availHeight = (parent.height + extraHeight) - unscaled - 28;//spacing
+ var rawDelta = (Math.max(0, availHeight - height) / 2);
+ return rawDelta + 14 - extraHeight;//spacing
+ }
+
+ header: RowLayout {
+ spacing: 14
+
+ TintIcon {
+ id: icon
+ property int iconType
+
+ Layout.preferredWidth: 24
+ Layout.preferredHeight: 24
+ Layout.margins: 24
+ Layout.bottomMargin: 0
+ Layout.rightMargin: 0
+ visible: msgConfig.subType != "about"
+ source: {
+ var base = "image://svg/de/skycoder42/qtmvvm/quick/icons/ic_%1";
+ switch(msgConfig.subType) {
+ case "information":
+ base = base.arg("info");
+ break;
+ case "question":
+ base = base.arg("help");
+ break;
+ case "warning":
+ base = base.arg("warning");
+ break;
+ case "critical":
+ base = base.arg("error");
+ break;
+ case "about":
+ default:
+ return "";
+ }
+ return base;
+ }
+ }
+
+ Label {
+ text: msgConfig.title
+ visible: msgConfig.title
+ elide: Label.ElideRight
+ font.bold: true
+ font.pixelSize: 16
+ Layout.fillWidth: true
+ Layout.margins: 24
+ Layout.bottomMargin: 0
+ Layout.leftMargin: icon.visible ? 0 : 24
+ }
+ }
+
+ Label {
+ id: _contentLabel
+ text: msgConfig.text.replace(/<\/p>/g, "
") //needed because qml does not put space between paragraphs...
+ visible: text != ""
+ anchors.fill: parent
+ wrapMode: Text.Wrap
+ onLinkActivated: Qt.openUrlExternally(link)
+ }
+
+ footer: DialogButtonBox {
+ id: _btnBox
+
+ readonly property var _allBtns: [
+ DialogButtonBox.NoButton,
+ DialogButtonBox.Ok,
+ DialogButtonBox.Save,
+ DialogButtonBox.SaveAll,
+ DialogButtonBox.Open,
+ DialogButtonBox.Yes,
+ DialogButtonBox.YesToAll,
+ DialogButtonBox.No,
+ DialogButtonBox.NoToAll,
+ DialogButtonBox.Abort,
+ DialogButtonBox.Retry,
+ DialogButtonBox.Ignore,
+ DialogButtonBox.Close,
+ DialogButtonBox.Cancel,
+ DialogButtonBox.Discard,
+ DialogButtonBox.Help,
+ DialogButtonBox.Apply,
+ DialogButtonBox.Reset,
+ DialogButtonBox.RestoreDefaults,
+ ]
+
+ standardButtons: msgConfig.buttons
+ onStandardButtonsChanged: {
+ for(var key in msgConfig.buttonTexts)
+ standardButton(DialogButtonBox.Ok).text = msgConfig.buttonTexts[key]
+ }
+
+ onClicked: {
+ if(msgResult) {
+ _allBtns.forEach(function(sBtn) {
+ if(button === standardButton(sBtn))
+ msgResult.complete(sBtn)
+ })
+ }
+ }
+ }
+}
diff --git a/src/imports/mvvmquick/QtMvvmApp.qml b/src/imports/mvvmquick/QtMvvmApp.qml
index b846a80..563d3cb 100644
--- a/src/imports/mvvmquick/QtMvvmApp.qml
+++ b/src/imports/mvvmquick/QtMvvmApp.qml
@@ -13,13 +13,18 @@ ApplicationWindow {
z: _rootStack.empty ? 10 : -10
}
+ PresentingStackView {
+ id: _rootStack
+ anchors.fill: parent
+ }
+
PopupPresenter {
id: _rootPopup
}
- PresentingStackView {
- id: _rootStack
- anchors.fill: parent
+ DialogPresenter {
+ id: _dialogs
+ rootItem: _root.contentItem
}
function presentDrawerContent(item) {
@@ -34,6 +39,10 @@ ApplicationWindow {
return _rootPopup.presentPopup(contentItem, popup);
}
+ function showDialog(config, result) {
+ return _dialogs.showDialog(config, result);
+ }
+
Component.onCompleted: QuickPresenter.qmlPresenter = _root
onClosing: {
diff --git a/src/imports/mvvmquick/TintIcon.qml b/src/imports/mvvmquick/TintIcon.qml
new file mode 100644
index 0000000..c7d7575
--- /dev/null
+++ b/src/imports/mvvmquick/TintIcon.qml
@@ -0,0 +1,39 @@
+import QtQuick 2.10
+import QtQuick.Controls.Material 2.3
+import QtQuick.Controls.Universal 2.3
+import QtGraphicalEffects 1.0
+import de.skycoder42.QtMvvm.Quick 1.0
+
+Item {
+ id: _tintIcon
+
+ property size iconSize: Qt.size(24, 24)
+ property alias tintColor: _overlay.color
+ property alias source: _image.source
+
+ Image {
+ id: _image
+ anchors.centerIn: parent
+ fillMode: Image.PreserveAspectFit
+ horizontalAlignment: Image.AlignHCenter
+ verticalAlignment: Image.AlignVCenter
+ width: iconSize.width
+ height: iconSize.height
+ sourceSize: iconSize
+ visible: false
+ }
+
+ ColorOverlay {
+ id: _overlay
+ anchors.fill: _image
+ source: _image
+ color: {
+ if(QuickPresenter.currentStyle === "Material")
+ return Material.foreground;
+ else if(QuickPresenter.currentStyle === "Universal")
+ return Universal.foreground;
+ else
+ return "black";
+ }
+ }
+}
diff --git a/src/imports/mvvmquick/mvvmquick.pro b/src/imports/mvvmquick/mvvmquick.pro
index 3fae484..1ce5f77 100644
--- a/src/imports/mvvmquick/mvvmquick.pro
+++ b/src/imports/mvvmquick/mvvmquick.pro
@@ -18,7 +18,10 @@ QML_FILES += \
QtMvvmApp.qml \
PresentingStackView.qml \
PresenterProgress.qml \
- PopupPresenter.qml
+ PopupPresenter.qml \
+ DialogPresenter.qml \
+ TintIcon.qml \
+ MsgBox.qml
OTHER_FILES += qmldir
diff --git a/src/imports/mvvmquick/qmldir b/src/imports/mvvmquick/qmldir
index 8299951..a56f8a0 100644
--- a/src/imports/mvvmquick/qmldir
+++ b/src/imports/mvvmquick/qmldir
@@ -3,8 +3,12 @@ plugin declarative_mvvmquick
classname QtMvvmQuickDeclarativeModule
typeinfo plugins.qmltypes
+internal TintIcon TintIcon.qml
+internal MsgBox MsgBox.qml
+
PresenterProgress 1.0 PresenterProgress.qml
PresentingStackView 1.0 PresentingStackView.qml
PopupPresenter 1.0 PopupPresenter.qml
+DialogPresenter 1.0 DialogPresenter.qml
QtMvvmApp 1.0 QtMvvmApp.qml
diff --git a/src/imports/mvvmquick/qqmlquickpresenter.cpp b/src/imports/mvvmquick/qqmlquickpresenter.cpp
index e13adf4..25edc03 100644
--- a/src/imports/mvvmquick/qqmlquickpresenter.cpp
+++ b/src/imports/mvvmquick/qqmlquickpresenter.cpp
@@ -60,6 +60,25 @@ void QQmlQuickPresenter::present(ViewModel *viewModel, const QVariantHash ¶m
}
}
+void QQmlQuickPresenter::showDialog(const MessageConfig &config, MessageResult *result)
+{
+ if(!_qmlPresenter) {
+ qmlWarning(this).space() << "No QML-Presenter registered! Unable to present dialog of type" //TODO use via define EVERYWHERE!!!
+ << config.type();
+ return;
+ }
+
+ QVariant res = false;
+ QMetaObject::invokeMethod(_qmlPresenter, "showDialog", Qt::DirectConnection,
+ Q_RETURN_ARG(QVariant, res),
+ Q_ARG(QVariant, QVariant::fromValue(config)),
+ Q_ARG(QVariant, QVariant::fromValue(result)));
+ if(!res.toBool()) {
+ qmlWarning(this).space() << "Failed to present dialog of type"
+ << config.type();
+ }
+}
+
void QQmlQuickPresenter::statusChanged(QQmlComponent::Status status)
{
auto component = qobject_cast(sender());
@@ -94,15 +113,15 @@ void QQmlQuickPresenter::statusChanged(QQmlComponent::Status status)
void QQmlQuickPresenter::addObject(QQmlComponent *component, ViewModel *viewModel, const QVariantHash ¶ms, QPointer parent)
{
if(!_qmlPresenter) {
- qCritical() << "No QML-Presenter registered! Unable to present control of type"
- << viewModel->metaObject()->className();
+ qmlWarning(this).space() << "No QML-Presenter registered! Unable to present viewModel of type"
+ << viewModel->metaObject()->className();
return;
}
//create the view item, set initial stuff and them complete creation
auto item = component->beginCreate(_engine->rootContext());
if(!item) {
- qmlWarning(this) << "Unable to create quick view from the loaded component"
+ qmlWarning(this).space() << "Unable to create quick view from the loaded component"
<< component->url();
return;
}
@@ -122,7 +141,7 @@ void QQmlQuickPresenter::addObject(QQmlComponent *component, ViewModel *viewMode
if(!item->parent())
QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership);
} else {
- qmlWarning(this) << "Failed to present item for viewModel of type"
+ qmlWarning(this).space() << "Failed to present item for viewModel of type"
<< viewModel->metaObject()->className();
item->deleteLater();
}
diff --git a/src/imports/mvvmquick/qqmlquickpresenter.h b/src/imports/mvvmquick/qqmlquickpresenter.h
index 025d9f8..e940028 100644
--- a/src/imports/mvvmquick/qqmlquickpresenter.h
+++ b/src/imports/mvvmquick/qqmlquickpresenter.h
@@ -12,6 +12,7 @@
#include
#include
+#include
namespace QtMvvm {
@@ -40,6 +41,7 @@ Q_SIGNALS:
private Q_SLOTS:
void present(QtMvvm::ViewModel *viewModel, const QVariantHash ¶ms, const QUrl &viewUrl, QPointer parent);
+ void showDialog(const QtMvvm::MessageConfig &config, QtMvvm::MessageResult *result);
void statusChanged(QQmlComponent::Status status);
private:
diff --git a/src/mvvmcore/message.cpp b/src/mvvmcore/message.cpp
index 8966582..3d18eb1 100644
--- a/src/mvvmcore/message.cpp
+++ b/src/mvvmcore/message.cpp
@@ -158,6 +158,22 @@ void MessageConfig::resetButtons()
d->buttonTexts.clear();
}
+QVariantMap MessageConfig::buttonTextsMap() const
+{
+ QVariantMap map;
+ for(auto it = d->buttonTexts.constBegin(); it != d->buttonTexts.constEnd(); it++)
+ map.insert(QString::number(it.key()), it.value());
+ return map;
+}
+
+void MessageConfig::setButtonTextsMap(const QVariantMap &buttonTexts)
+{
+ QHash map;
+ for(auto it = buttonTexts.constBegin(); it != buttonTexts.constEnd(); it++)
+ map.insert(static_cast(it.key().toInt()), it.value().toString());
+ d->buttonTexts = map;
+}
+
MessageResult::MessageResult() :
diff --git a/src/mvvmcore/message.h b/src/mvvmcore/message.h
index e143e0a..21f8605 100644
--- a/src/mvvmcore/message.h
+++ b/src/mvvmcore/message.h
@@ -25,7 +25,7 @@ class Q_MVVMCORE_EXPORT MessageConfig
Q_PROPERTY(QString title READ title WRITE setTitle)
Q_PROPERTY(QString text READ text WRITE setText)
Q_PROPERTY(StandardButtons buttons READ buttons WRITE setButtons RESET resetButtons)
- Q_PROPERTY(QHash buttonTexts READ buttonTexts WRITE setButtonTexts RESET resetButtons)
+ Q_PROPERTY(QVariantMap buttonTexts READ buttonTextsMap WRITE setButtonTextsMap RESET resetButtons)
Q_PROPERTY(QVariant defaultValue READ defaultValue WRITE setDefaultValue)
Q_PROPERTY(QVariantMap viewProperties READ viewProperties WRITE setViewProperties)
@@ -103,6 +103,9 @@ public:
private:
QSharedDataPointer d;
+
+ QVariantMap buttonTextsMap() const;
+ void setButtonTextsMap(const QVariantMap &buttonTexts);
};
class MessageResultPrivate;
diff --git a/src/mvvmquick/quickpresenter.cpp b/src/mvvmquick/quickpresenter.cpp
index a5478c1..58941c3 100644
--- a/src/mvvmquick/quickpresenter.cpp
+++ b/src/mvvmquick/quickpresenter.cpp
@@ -46,7 +46,12 @@ void QuickPresenter::present(QtMvvm::ViewModel *viewModel, const QVariantHash &p
void QuickPresenter::showDialog(const QtMvvm::MessageConfig &config, QtMvvm::MessageResult *result)
{
- qDebug(Q_FUNC_INFO);
+ if(d->qmlPresenter) {
+ QMetaObject::invokeMethod(d->qmlPresenter, "showDialog",
+ Q_ARG(QtMvvm::MessageConfig, config),
+ Q_ARG(QtMvvm::MessageResult*, result));
+ } else
+ throw PresenterException("QML presenter not ready - cannot present yet");
}
bool QuickPresenter::presentToQml(QObject *qmlPresenter, QObject *viewObject)