diff --git a/examples/mvvmdatasyncquick/DataSyncSampleQuick/SampleView.qml b/examples/mvvmdatasyncquick/DataSyncSampleQuick/SampleView.qml index a6af9cf..ab610a0 100644 --- a/examples/mvvmdatasyncquick/DataSyncSampleQuick/SampleView.qml +++ b/examples/mvvmdatasyncquick/DataSyncSampleQuick/SampleView.qml @@ -1,9 +1,9 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 -import de.skycoder42.QtMvvm.Core 1.0 -import de.skycoder42.QtMvvm.Quick 1.0 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 +import de.skycoder42.QtMvvm.Sample 1.1 Page { id: sampleView diff --git a/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.cpp b/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.cpp index 25a3847..b6e9a11 100644 --- a/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.cpp +++ b/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.cpp @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); QtMvvm::registerDataSyncQuick(); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "SampleViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "SampleViewModel", QStringLiteral("ViewModels cannot be created")); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.qml b/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.qml index a60e825..36878b4 100644 --- a/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.qml +++ b/examples/mvvmdatasyncquick/DataSyncSampleQuick/main.qml @@ -1,9 +1,8 @@ import QtQuick 2.10 -import de.skycoder42.QtMvvm.Quick 1.0 import QtQuick.Controls.Material 2.3 +import de.skycoder42.QtMvvm.Quick 1.1 QtMvvmApp { - Material.accent: Material.DeepPurple Material.primary: Material.Lime diff --git a/examples/mvvmquick/SampleQuick/DrawerView.qml b/examples/mvvmquick/SampleQuick/DrawerView.qml index 85f5732..9e05ba8 100644 --- a/examples/mvvmquick/SampleQuick/DrawerView.qml +++ b/examples/mvvmquick/SampleQuick/DrawerView.qml @@ -1,6 +1,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Sample 1.1 ListView { id: drawerView diff --git a/examples/mvvmquick/SampleQuick/ResultView.qml b/examples/mvvmquick/SampleQuick/ResultView.qml index 2efce58..a11a539 100644 --- a/examples/mvvmquick/SampleQuick/ResultView.qml +++ b/examples/mvvmquick/SampleQuick/ResultView.qml @@ -1,9 +1,9 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 -import de.skycoder42.QtMvvm.Core 1.0 -import de.skycoder42.QtMvvm.Quick 1.0 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 +import de.skycoder42.QtMvvm.Sample 1.1 AlertDialog { id: resultDialog diff --git a/examples/mvvmquick/SampleQuick/SampleView.qml b/examples/mvvmquick/SampleQuick/SampleView.qml index 0d36d80..561d6ee 100644 --- a/examples/mvvmquick/SampleQuick/SampleView.qml +++ b/examples/mvvmquick/SampleQuick/SampleView.qml @@ -1,9 +1,9 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 -import de.skycoder42.QtMvvm.Core 1.0 -import de.skycoder42.QtMvvm.Quick 1.0 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 +import de.skycoder42.QtMvvm.Sample 1.1 Page { id: sampleView diff --git a/examples/mvvmquick/SampleQuick/TabItemView.qml b/examples/mvvmquick/SampleQuick/TabItemView.qml index b97f0dc..61eb45d 100644 --- a/examples/mvvmquick/SampleQuick/TabItemView.qml +++ b/examples/mvvmquick/SampleQuick/TabItemView.qml @@ -1,8 +1,8 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import de.skycoder42.QtMvvm.Core 1.0 -import de.skycoder42.QtMvvm.Quick 1.0 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 +import de.skycoder42.QtMvvm.Sample 1.1 Pane { property TabItemViewModel viewModel: null diff --git a/examples/mvvmquick/SampleQuick/TabView.qml b/examples/mvvmquick/SampleQuick/TabView.qml index f0736d6..4954862 100644 --- a/examples/mvvmquick/SampleQuick/TabView.qml +++ b/examples/mvvmquick/SampleQuick/TabView.qml @@ -2,9 +2,9 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Controls.Material 2.3 import QtQuick.Layouts 1.3 -import de.skycoder42.QtMvvm.Core 1.0 -import de.skycoder42.QtMvvm.Quick 1.0 -import de.skycoder42.QtMvvm.Sample 1.0 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 +import de.skycoder42.QtMvvm.Sample 1.1 Page { id: tabView diff --git a/examples/mvvmquick/SampleQuick/main.cpp b/examples/mvvmquick/SampleQuick/main.cpp index eb19b75..9db145f 100644 --- a/examples/mvvmquick/SampleQuick/main.cpp +++ b/examples/mvvmquick/SampleQuick/main.cpp @@ -29,11 +29,11 @@ int main(int argc, char *argv[]) coreApp->setShowDrawer(true); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "SampleViewModel", QStringLiteral("ViewModels cannot be created")); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "ResultViewModel", QStringLiteral("ViewModels cannot be created")); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "DrawerViewModel", QStringLiteral("ViewModels cannot be created")); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "TabViewModel", QStringLiteral("ViewModels cannot be created")); - qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 0, "TabItemViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "SampleViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "ResultViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "DrawerViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "TabViewModel", QStringLiteral("ViewModels cannot be created")); + qmlRegisterUncreatableType("de.skycoder42.QtMvvm.Sample", 1, 1, "TabItemViewModel", QStringLiteral("ViewModels cannot be created")); QtMvvm::ServiceRegistry::instance()->registerObject(); QtMvvm::ServiceRegistry::instance()->registerInterface(); diff --git a/examples/mvvmquick/SampleQuick/main.qml b/examples/mvvmquick/SampleQuick/main.qml index 10fb363..8d7247c 100644 --- a/examples/mvvmquick/SampleQuick/main.qml +++ b/examples/mvvmquick/SampleQuick/main.qml @@ -1,5 +1,5 @@ import QtQuick 2.10 -import de.skycoder42.QtMvvm.Quick 1.0 +import de.skycoder42.QtMvvm.Quick 1.1 QtMvvmApp { title: qsTr("QtMvvm Quick Sample") diff --git a/src/imports/mvvmquick/DialogPresenter.qml b/src/imports/mvvmquick/DialogPresenter.qml index bd1db1f..7bf4fbc 100644 --- a/src/imports/mvvmquick/DialogPresenter.qml +++ b/src/imports/mvvmquick/DialogPresenter.qml @@ -56,9 +56,6 @@ QtObject { */ property Item rootItem: null - //TODO document - readonly property bool empty: _popups.length == 0 - /*! @brief The primary presenting method to present a dialog * * @param type:MessageConfig config The message configuration to create a dialog of diff --git a/src/imports/mvvmquick/DialogPresenter11.qml b/src/imports/mvvmquick/DialogPresenter11.qml new file mode 100644 index 0000000..522f1ef --- /dev/null +++ b/src/imports/mvvmquick/DialogPresenter11.qml @@ -0,0 +1,237 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import de.skycoder42.QtMvvm.Core 1.1 +import de.skycoder42.QtMvvm.Quick 1.1 + +/*! @brief A presentation helper that can present generic mvvm dialogs + * + * @extends QtQml.QtObject + * + * @details You can use this class as presenter for dialogs in case you create your own root + * presenter instead of using the QtMvvmApp. This partial presenter can handle simple dialogs + * (the ones shown via QtMvvm::CoreApp::showDialog). Use it as follows: + * + * @code{.qml} + * MyPresenter { + * id: _root + * + * DialogPresenter { + * id: _rootDialogs + * rootItem: _root.contentItem + * } + * + * function showDialog(config, result) { + * return _rootDialogs.showDialog(config, result); + * } + * + * function closeAction() { + * var closed = false; + * // ... + * if(!closed) + * closed = _rootDialogs.closeAction(); + * // ... + * return closed; + * } + * } + * @endcode + * + * @sa QtMvvmApp + */ +QtObject { + id: _dialogPresenter + + /*! @brief The root item to present dialogs to + * + * @default{`null`} + * + * This propety **must** be set in order for the presenter to work. It will create new + * dialogs with that item as parent, which decides in which relative are they are + * displayed. Unless you want to explicitly reposition dialogs, this should always be the + * root item of the root component of your application. + * + * @accessors{ + * @memberAc{rootItem} + * @notifyAc{rootItemChanged()} + * } + */ + property Item rootItem: null + + //TODO document + readonly property bool empty: _popups.length == 0 + + /*! @brief The primary presenting method to present a dialog + * + * @param type:MessageConfig config The message configuration to create a dialog of + * @param type:MessageResult result The result to report the dialog result to + * @return type:bool `true` if successfully presented, `false` if not + * + * Use this method in your main presenter to present dialogs via this sub presenter. + * + * @sa QtMvvm::MessageConfig, QtMvvm::MessageResult, QtMvvmApp::showDialog + */ + function showDialog(config, result) { + if(config.type == "msgbox") + return createMsgBox(config, result) + else if(config.type == "input") + return createInput(config, result) + else if(config.type == "file") + return createFile(config, result) + else + return false; + } + + /*! @brief Can be called to try to close open dialogs + * + * @return type:bool `true` if a dialog was closed, `false` if not + * + * Use this method in your main presenter react on a close event. It will close the + * latest top level dialog, if one is present. + * + * @sa QtMvvmApp::closeAction + */ + function closeAction() { + if(_popups.length > 0) { + _popups[_popups.length - 1].reject(); + return true; + } else + return false; + } + + //! Internal property + property var _popups: [] + //! Internal property + property Component _msgBoxComponent: MsgBox { + id: __msgBox + + onClosed: { + var index = _popups.indexOf(__msgBox); + if(index > -1) { + __msgBox.destroy(); + _dialogPresenter._popups.splice(index, 1); + } + } + + Component.onCompleted: { + _popups.push(__msgBox) + __msgBox.open() + } + } + + //! Internal property + property Component _inputComponent: InputDialog { + id: __input + + onClosed: { + var index = _popups.indexOf(__input); + if(index > -1) { + __input.destroy(); + _dialogPresenter._popups.splice(index, 1); + } + } + + Component.onCompleted: { + _popups.push(__input) + __input.open() + } + } + + //! Internal property + property Component _fileComponent: FileDialog { + id: __file + + onClosed: { + var index = _popups.indexOf(__file); + if(index > -1) { + __file.destroy(); + _dialogPresenter._popups.splice(index, 1); + } + } + + Component.onCompleted: { + _popups.push(__file) + __file.open() + } + } + + //! Internal property + property Component _folderComponent: FolderDialog { + id: __folder + + onClosed: { + var index = _popups.indexOf(__folder); + if(index > -1) { + __folder.destroy(); + _dialogPresenter._popups.splice(index, 1); + } + } + + Component.onCompleted: { + _popups.push(__folder) + __folder.open() + } + } + + /*! @brief Method present a dialog of the QtMvvm::MessageConfig::TypeMessageBox + * + * @param type:MessageConfig config The message configuration to create a dialog of + * @param type:MessageResult result The result to report the dialog result to + * @return type:bool `true` if successfully presented, `false` if not + * + * Used by the showDialog() method to show a dialog of the message box type. You can use + * it yourself if you want to extend the presenter and still keep the default dialogs it + * supports. + * + * @sa DialogPresenter::showDialog + */ + function createMsgBox(config, result) { + var props = config.viewProperties; + props["msgConfig"] = config; + props["msgResult"] = result; + var incubator = _msgBoxComponent.incubateObject(rootItem, props, Qt.Synchronous); + return incubator.status !== Component.Error; + } + + /*! @brief Method present a dialog of the QtMvvm::MessageConfig::TypeInputDialog + * + * @param type:MessageConfig config The message configuration to create a dialog of + * @param type:MessageResult result The result to report the dialog result to + * @return type:bool `true` if successfully presented, `false` if not + * + * Used by the showDialog() method to show a dialog of the input dialog type. You can use + * it yourself if you want to extend the presenter and still keep the default dialogs it + * supports. + * + * @sa DialogPresenter::showDialog + */ + function createInput(config, result) { + var props = config.viewProperties; + props["msgConfig"] = config; + props["msgResult"] = result; + var incubator = _inputComponent.incubateObject(rootItem, props, Qt.Synchronous); + return incubator.status !== Component.Error; + } + + /*! @brief Method present a dialog of the QtMvvm::MessageConfig::TypeFileDialog + * + * @param type:MessageConfig config The message configuration to create a dialog of + * @param type:MessageResult result The result to report the dialog result to + * @return type:bool `true` if successfully presented, `false` if not + * + * Used by the showDialog() method to show a dialog of the file dialog type. You can use + * it yourself if you want to extend the presenter and still keep the default dialogs it + * supports. + * + * @sa DialogPresenter::showDialog + */ + function createFile(config, result) { + var props = config.viewProperties; + props["msgConfig"] = config; + props["msgResult"] = result; + var incubator = null; + if(config.subType == "folder") + incubator = _folderComponent.incubateObject(rootItem, props, Qt.Synchronous); + else + incubator = _fileComponent.incubateObject(rootItem, props, Qt.Synchronous); + return incubator.status !== Component.Error; + } +} diff --git a/src/imports/mvvmquick/PopupPresenter.qml b/src/imports/mvvmquick/PopupPresenter.qml index f631f81..6b1446a 100644 --- a/src/imports/mvvmquick/PopupPresenter.qml +++ b/src/imports/mvvmquick/PopupPresenter.qml @@ -54,10 +54,7 @@ QtObject { * } */ property Item rootItem: null - - //TODO document - readonly property bool empty: _popups.length == 0 - + //! Internal property property var _popups: [] diff --git a/src/imports/mvvmquick/PopupPresenter11.qml b/src/imports/mvvmquick/PopupPresenter11.qml new file mode 100644 index 0000000..f631f81 --- /dev/null +++ b/src/imports/mvvmquick/PopupPresenter11.qml @@ -0,0 +1,103 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +/*! @brief A presentation helper that can present mvvm views that extend the + * @ref QtQuick.Controls.Popup "Popup" type + * + * @extends QtQml.QtObject + * + * @details You can use this class as presenter for popup views in case you create your own + * root presenter instead of using the QtMvvmApp. This partial presenter can handle any view + * that extends the popup type. Use it as follows: + * + * @code{.qml} + * MyPresenter { + * id: _root + * + * PopupPresenter { + * id: _rootPopup + * rootItem: _root.contentItem + * } + * + * function presentPopup(popup) { + * return _rootPopup.presentPopup(popup); + * } + * + * function closeAction() { + * var closed = false; + * // ... + * if(!closed) + * closed = _rootPopup.closeAction(); + * // ... + * return closed; + * } + * } + * @endcode + * + * @sa QtMvvmApp + */ +QtObject { + id: _popPresenter + + /*! @brief The root item to present popup views to + * + * @default{`null`} + * + * This propety **must** be set in order for the presenter to work. It will create new + * popups with that item as parent, which decides in which relative are they are + * displayed. Unless you want to explicitly reposition popups, this should always be the + * root item of the root component of your application. + * + * @accessors{ + * @memberAc{rootItem} + * @notifyAc{rootItemChanged()} + * } + */ + property Item rootItem: null + + //TODO document + readonly property bool empty: _popups.length == 0 + + //! Internal property + property var _popups: [] + + /*! @brief The primary presenting method to present a popup view + * + * @param type:Popup popup The popup to be presented + * @return type:bool `true` if successfully presented, `false` if not + * + * Use this method in your main presenter to present popups via this sub presenter. + * + * @sa QtMvvmApp::presentPopup, @ref QtQuick.Controls.Popup "Popup" + */ + function presentPopup(popup) { + popup.parent = rootItem; + popup.closed.connect(function() { + var index = _popups.indexOf(popup); + if(index > -1) { + popup.destroy(); + _popups.splice(index, 1); + } + }); + _popups.push(popup); + popup.open(); + return true; + } + + /*! @brief Can be called to try to close open popups + * + * @return type:bool `true` if a popup was closed, `false` if not + * + * Use this method in your main presenter react on a close event. It will close the + * latest top level popup, if one is present. + * + * @sa QtMvvmApp::closeAction + */ + function closeAction() { + if(_popups.length > 0) { + _popups[_popups.length - 1].close(); + return true; + } else + return false; + } +} diff --git a/src/imports/mvvmquick/PresentingStackView.qml b/src/imports/mvvmquick/PresentingStackView.qml index aa2a033..94ac8f1 100644 --- a/src/imports/mvvmquick/PresentingStackView.qml +++ b/src/imports/mvvmquick/PresentingStackView.qml @@ -113,10 +113,14 @@ StackView { return true; } - if(_presenterStack.safePop()) - return true; - else + if(_presenterStack.depth <= 1) return false; + else { + if(_presenterStack.safePop()) + return true; + else + return false; + } } /*! @brief Pop and delete a view diff --git a/src/imports/mvvmquick/PresentingStackView11.qml b/src/imports/mvvmquick/PresentingStackView11.qml new file mode 100644 index 0000000..aa2a033 --- /dev/null +++ b/src/imports/mvvmquick/PresentingStackView11.qml @@ -0,0 +1,240 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 + +/*! @brief A presentation helper that can present standard mvvm views + * + * @extends QtQuick.Controls.StackView + * + * @details You can use this class as presenter for normale views in case you create your own + * root presenter instead of using the QtMvvmApp. This partial presenter can handle any view + * that extends the @ref QtQuick.Item "Item" type. Use it as follows: + * + * @code{.qml} + * MyPresenter { + * id: _root + * + * PresentingStackView { + * id: _rootStack + * anchors.fill: parent + * } + * + * function presentItem(item) { + * return _rootStack.presentItem(item); + * } + * + * function closeAction() { + * var closed = false; + * // ... + * if(!closed) + * closed = _rootStack.closeAction(); + * // ... + * return closed; + * } + * } + * @endcode + * + * @sa QtMvvmApp + */ +StackView { + id: _presenterStack + + /*! @brief The duration of the default push and pop animations + * + * @default{`150` milliseconds} + * + * You can use this property to speed up or slow down the default animations + * + * @accessors{ + * @memberAc{animDuration} + * @notifyAc{animDurationChanged()} + * } + * + * @sa opDuration + */ + property int animDuration: 150 + /*! @brief The duration of the fade out + * + * @default{`75` milliseconds} + * + * You can use this property to speed up or slow down the fading part of the push and + * pop animations. + * + * @accessors{ + * @memberAc{opDuration} + * @notifyAc{opDurationChanged()} + * } + * + * @sa animDuration + */ + property int opDuration: 75 + + //! Internal property + property var _clearItems: [] + + /*! @brief The primary presenting method to present a view + * + * @param type:Item item The item to be presented + * @return type:bool `true` if successfully presented, `false` if not + * + * Use this method in your main presenter to present items via this sub presenter. + * + * If the presented item has a boolean property named `presentAsRoot`, the item will + * replace all contents on the stack instead of beeing pushed on it. + * + * @sa QtMvvmApp::presentItem, @ref QtQuick.Item "Item" + */ + function presentItem(item) { + if(typeof item.presentAsRoot == "boolean" && item.presentAsRoot) { + if(safeReplace(null, item)) + return true; + else + return false; + } else { + if(push(item)) + return true; + else + return false; + } + } + + /*! @brief Can be called to try to close the presenters current top level view + * + * @return type:bool `true` if a view was closed, `false` if not + * + * Use this method in your main presenter react on a close event. It will pop the top + * level view from the stack, if one is present + * + * @sa QtMvvmApp::closeAction + */ + function closeAction() { + if(_presenterStack.currentItem && + typeof _presenterStack.currentItem.closeAction == "function") { + if(_presenterStack.currentItem.closeAction()) + return true; + } + + if(_presenterStack.safePop()) + return true; + else + return false; + } + + /*! @brief Pop and delete a view + * + * @param type:Item item an optional item to be popped to + * @param type:int operation How to perform the operation + * @return type:bool `true` if a view was closed, `false` if not + * + * Use this method instead of the @ref QtQuick.Controls.StackView "pop()" method. It will + * not only pop the top level view, but also delete it. This is recommened to do for views + * presented. + * + * @sa PresentingStackView::closeAction, PresentingStackView::safeReplace + */ + function safePop(item, operation) { + var resItem = pop(item, operation) + if(resItem) { + _clearItems.push(resItem); + return true; + } else + return false; + } + + /*! @brief replace and delete views + * + * @param type:Item target The new view to be placed on the stack + * @param type:Item item an optional item to be popped to + * @param type:object properties The presentation properties to be passed to the target + * @param type:int operation How to perform the operation + * @return type:bool `true` if the replacement was successful, `false` if not + * + * Use this method instead of the @ref QtQuick.Controls.StackView "replace()" method. It + * will not only replace the replaced views, but also delete them. This is recommened to + * do for views presented. + * + * @sa PresentingStackView::closeAction, PresentingStackView::safePop + */ + function safeReplace(target, item, properties, operation) { + var items = []; + console.log("current depth: ", depth) + for(var i = depth -1; i >= 0; i--) { + var cItem = get(i, StackView.ForceLoad); + _clearItems.push(cItem); + if(cItem === target) + break; + } + if(replace(target, item, properties, operation)) + return true; + else + return false; + } + + //! Internal method + function clearWaitingItems() { + _clearItems.forEach(function(item) { + if(typeof item.afterPop == "function") + item.afterPop(); + item.destroy(); + }); + _clearItems = []; + } + + pushEnter: Transition { + PropertyAnimation { + property: "y" + easing.type: Easing.InOutQuad + from: height * 0.3 + to: 0 + duration: _presenterStack.animDuration + } + PropertyAnimation { + property: "opacity" + from: 0.0 + to: 1.0 + duration: _presenterStack.opDuration + } + } + pushExit: Transition { + PauseAnimation { + duration: _presenterStack.animDuration + } + + onRunningChanged: { + if(!running) + Qt.callLater(clearWaitingItems) + } + } + popEnter: Transition { + PauseAnimation { + duration: _presenterStack.animDuration + } + } + popExit: Transition { + PropertyAnimation { + property: "y" + easing.type: Easing.InOutQuad + from: 0 + to: height * 0.3 + duration: _presenterStack.animDuration + } + SequentialAnimation { + PauseAnimation { + duration: _presenterStack.animDuration - _presenterStack.opDuration + } + PropertyAnimation { + id: pp1 + property: "opacity" + from: 1.0 + to: 0.0 + duration: _presenterStack.opDuration + } + } + + onRunningChanged: { + if(!running) + Qt.callLater(clearWaitingItems) + } + } + replaceEnter: pushEnter + replaceExit: pushExit +} diff --git a/src/imports/mvvmquick/QtMvvmApp.qml b/src/imports/mvvmquick/QtMvvmApp.qml index 0abe16b..9e6e17e 100644 --- a/src/imports/mvvmquick/QtMvvmApp.qml +++ b/src/imports/mvvmquick/QtMvvmApp.qml @@ -172,9 +172,6 @@ ApplicationWindow { closed = _drawerLoader.item.closeAction(); if(!closed) closed = _rootStack.closeAction(); - //if everything was closed -> still accept it - if(closed && _rootDialogs.emtpy && _rootPopup.empty && _rootStack.empty) - closed = false; return closed; } diff --git a/src/imports/mvvmquick/QtMvvmApp11.qml b/src/imports/mvvmquick/QtMvvmApp11.qml new file mode 100644 index 0000000..0ae6f49 --- /dev/null +++ b/src/imports/mvvmquick/QtMvvmApp11.qml @@ -0,0 +1,184 @@ +import QtQuick 2.10 +import QtQuick.Controls 2.3 +import de.skycoder42.QtMvvm.Quick 1.1 + +/*! @brief An application root window that is a full fledged QML presenter + * + * @extends QtQuick.Controls.ApplicationWindow + * + * @details This is the standard presenter you should use in your mvvm application as the root + * QML component of the primary qml file loaded by the engine. It will automatically register + * as the main QML presenter and can handle all the standard view types. + * + * @sa PresenterProgress, PresentingStackView, PresentingDrawer, PopupPresenter, + * DialogPresenter, QuickPresenter + */ +ApplicationWindow { + id: _root + visible: true + width: 360 + height: 520 + + /*! @brief type:PresentingDrawer A reference to the PresentingDrawer + * + * @default{`null`} + * + * Returns the PresentingDrawer that is beeing used by the app. It is null by default as + * the presenter only gets created the first time a drawer view is beeing shown. From that + * point on, you can access the drawer via this property. + * + * @accessors{ + * @memberAc{drawer} + * @notifyAc{drawerChanged()} + * @readonlyAc + * } + * + * @sa PresentingDrawer, QtMvvmApp::presentDrawerContent, QtMvvmApp::rootOnlyDrawer + */ + readonly property alias drawer: _drawerLoader.item + /*! @brief Specifies if the drawer can be accessed from the root element only + * + * @default{`true`} + * + * If set to `true`, the presenter can only be accessed by the user if only one view is + * currently presented on the internal PresentingStackView. As soon as second view gets + * pushed, the user cannot open the drawer anymore. By settings it to `false`, the drawer + * will always be accessible on any view on the stack. + * + * @accessors{ + * @memberAc{rootOnlyDrawer} + * @notifyAc{rootOnlyDrawerChanged()} + * } + * + * @sa QtMvvmApp::presentDrawerContent, QtMvvmApp::drawer, QtMvvmApp::presentItem + */ + property bool rootOnlyDrawer: true + + PresenterProgress { + id: _rootProgress + z: _rootStack.empty ? 10 : -10 + } + + PresentingStackView { + id: _rootStack + anchors.fill: parent + } + + Loader { + id: _drawerLoader + active: false + asynchronous: false + sourceComponent: PresentingDrawer { + id: _rootDrawer + + interactive: !_root.rootOnlyDrawer || _rootStack.depth == 1 + } + } + + PopupPresenter { + id: _rootPopup + rootItem: _root.contentItem + } + + DialogPresenter { + id: _rootDialogs + rootItem: _root.contentItem + } + + /*! @brief The presenting method to present a drawer item + * + * @param type:Item item The item to be shown + * @return type:bool `true` if successfully presented, `false` if not + * + * Calls PresentingDrawer::presentDrawerContent on the internally used presenting drawer. + * If no drawer has been presented yet, it is automatically created. + * + * @sa PresentingDrawer::presentDrawerContent, QtMvvmApp::drawer, @ref QtQuick.Item "Item" + */ + function presentDrawerContent(item) { + if(!_drawerLoader.item) + _drawerLoader.active = true; + return _drawerLoader.item.presentDrawerContent(item); + } + + /*! @brief The presenting method to present a view + * + * @param type:Item item The item to be presented + * @return type:bool `true` if successfully presented, `false` if not + * + * Calls PresentingStackView::presentItem on the internally used presenting stack view. + * + * @sa PresentingStackView::presentItem, @ref QtQuick.Item "Item" + */ + function presentItem(item) { + return _rootStack.presentItem(item); + } + + /*! @brief The presenting method to present a popup view + * + * @param type:Popup popup The popup to be presented + * @return type:bool `true` if successfully presented, `false` if not + * + * Calls PopupPresenter::presentPopup on the internally used popup presenter. + * + * @sa PopupPresenter::presentPopup, @ref QtQuick.Controls.Popup "Popup" + */ + function presentPopup(popup) { + return _rootPopup.presentPopup(popup); + } + + /*! @brief The presenting method to present a dialog + * + * @param type:MessageConfig config The message configuration to create a dialog of + * @param type:MessageResult result The result to report the dialog result to + * @return type:bool `true` if successfully presented, `false` if not + * + * Calls DialogPresenter::showDialog on the internally used dialog presenter. + * + * @sa QtMvvm::MessageConfig, QtMvvm::MessageResult, DialogPresenter::showDialog + */ + function showDialog(config, result) { + return _rootDialogs.showDialog(config, result); + } + + //! @copybrief PresentingDrawer::toggle + function toggleDrawer() { + if(_drawerLoader.item) + _drawerLoader.item.toggle(); + else + console.warn("No drawer like view active. Cannot toggle drawer"); + } + + /*! @brief Can be called to perform a close operation + * + * @return type:bool `true` if anything was closed, `false` if not + * + * Use this method to perform a close operation. The app will automatically check all + * internally used presenter in a logical order and call this method on all of them until + * one returns with a positive result. This way you have a global method to close any kind + * of active view. This is also used to handle window close events and the mobile back + * button. + * + * @sa PopupPresenter::closeAction, DialogPresenter::closeAction, + * PresentingDrawer::closeAction, PresentingStackView::closeAction + */ + function closeAction() { + var closed = false; + if(!closed) + closed = _rootDialogs.closeAction(); + if(!closed) + closed = _rootPopup.closeAction(); + if(!closed && _drawerLoader.item) + closed = _drawerLoader.item.closeAction(); + if(!closed) + closed = _rootStack.closeAction(); + //if everything was closed -> still accept it + if(closed && _rootDialogs.emtpy && _rootPopup.empty && _rootStack.empty) + closed = false; + return closed; + } + + Component.onCompleted: QuickPresenter.qmlPresenter = _root + + onClosing: close.accepted = !closeAction(); +} diff --git a/src/imports/mvvmquick/mvvmquick.pro b/src/imports/mvvmquick/mvvmquick.pro index 1856a1f..7d11bfc 100644 --- a/src/imports/mvvmquick/mvvmquick.pro +++ b/src/imports/mvvmquick/mvvmquick.pro @@ -24,10 +24,14 @@ SOURCES += \ QML_FILES += \ QtMvvmApp.qml \ + QtMvvmApp11.qml \ PresentingStackView.qml \ + PresentingStackView11.qml \ PresenterProgress.qml \ PopupPresenter.qml \ + PopupPresenter11.qml \ DialogPresenter.qml \ + DialogPresenter11.qml \ TintIcon.qml \ AlertDialog.qml \ ContrastToolBar.qml \ diff --git a/src/imports/mvvmquick/qmldir b/src/imports/mvvmquick/qmldir index e4cbd26..5d97676 100644 --- a/src/imports/mvvmquick/qmldir +++ b/src/imports/mvvmquick/qmldir @@ -25,10 +25,14 @@ MenuButton 1.0 MenuButton.qml PresenterProgress 1.0 PresenterProgress.qml PresentingStackView 1.0 PresentingStackView.qml +PresentingStackView 1.1 PresentingStackView11.qml PopupPresenter 1.0 PopupPresenter.qml +PopupPresenter 1.1 PopupPresenter11.qml DialogPresenter 1.0 DialogPresenter.qml +DialogPresenter 1.1 DialogPresenter11.qml PresentingDrawer 1.0 PresentingDrawer.qml SettingsView 1.0 SettingsView.qml QtMvvmApp 1.0 QtMvvmApp.qml +QtMvvmApp 1.1 QtMvvmApp11.qml