From 84c529d58d80f6cd26455db835f9f4ec2b7d2f76 Mon Sep 17 00:00:00 2001 From: Mikhail Rogachev Date: Mon, 27 Jun 2022 19:23:36 +0300 Subject: [PATCH] feat(StatusStackModal): Component to replace nested StausModals (#733) --- src/StatusQ/Core/StatusAnimatedStack.qml | 80 +++++++++++++++++ src/StatusQ/Core/qmldir | 1 + src/StatusQ/Popups/StatusModal.qml | 6 +- src/StatusQ/Popups/StatusStackModal.qml | 109 +++++++++++++++++++++++ src/StatusQ/Popups/qmldir | 1 + 5 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 src/StatusQ/Core/StatusAnimatedStack.qml create mode 100644 src/StatusQ/Popups/StatusStackModal.qml diff --git a/src/StatusQ/Core/StatusAnimatedStack.qml b/src/StatusQ/Core/StatusAnimatedStack.qml new file mode 100644 index 00000000..ba86e22c --- /dev/null +++ b/src/StatusQ/Core/StatusAnimatedStack.qml @@ -0,0 +1,80 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +StackLayout { + id: root + + property int previousIndex: 0 + property int duration: 300 + + property var items: children + + readonly property var previousItem: items[previousIndex] + readonly property var currentItem: items[currentIndex] + + clip: true + + Component.onCompleted: { + previousIndex = currentIndex + + for(var i = 1; i < count; ++i) { + children[i].opacity = 0 + } + } + + Component { + id: crossFader + + ParallelAnimation { + property Item fadeOutTarget + property Item fadeInTarget + readonly property bool direct: previousIndex < currentIndex + + NumberAnimation { + target: fadeOutTarget + property: "opacity" + to: 0 + duration: root.duration + } + + NumberAnimation { + target: fadeInTarget + property: "opacity" + to: 1 + duration: root.duration + } + + NumberAnimation { + target: fadeOutTarget + property: "x" + from: 0 + to: direct ? -root.width : root.width + duration: root.duration + } + + NumberAnimation { + target: fadeInTarget + property: "x" + from: direct ? root.width : -root.width + to: 0 + duration: root.duration + } + } + } + + onCurrentIndexChanged: { + items = root.children; + + if (previousItem && currentItem && (previousItem != currentItem)) { + previousItem.visible = true; + currentItem.visible = true; + var crossFaderAnim = crossFader.createObject(parent, + { + fadeOutTarget: previousItem, + fadeInTarget: currentItem + }); + crossFaderAnim.restart(); + } + previousIndex = currentIndex; + } +} \ No newline at end of file diff --git a/src/StatusQ/Core/qmldir b/src/StatusQ/Core/qmldir index a3152182..966c811e 100644 --- a/src/StatusQ/Core/qmldir +++ b/src/StatusQ/Core/qmldir @@ -9,3 +9,4 @@ StatusImageSettings 0.1 StatusImageSettings.qml StatusModalHeaderSettings 0.1 StatusModalHeaderSettings.qml StatusTooltipSettings 0.1 StatusTooltipSettings.qml StatusAppNavBarFilterModel 0.1 StatusAppNavBarFilterModel.qml +StatusAnimatedStack 0.1 StatusAnimatedStack.qml diff --git a/src/StatusQ/Popups/StatusModal.qml b/src/StatusQ/Popups/StatusModal.qml index 66065d3c..0cb83606 100644 --- a/src/StatusQ/Popups/StatusModal.qml +++ b/src/StatusQ/Popups/StatusModal.qml @@ -199,6 +199,7 @@ QC.Popup { parent: QC.Overlay.overlay width: 480 + // implicitHeight: headerImpl.implicitHeight + contentItem.implicitHeight + footerImpl.implicitHeight padding: 0 topPadding: padding + headerImpl.implicitHeight @@ -214,7 +215,6 @@ QC.Popup { color: Theme.palette.backdropColor } - background: Rectangle { color: Theme.palette.statusModal.backgroundColor radius: 8 @@ -245,7 +245,7 @@ QC.Popup { anchors.top: parent.top anchors.topMargin: hasFloatingButtons ? -18 - height : 0 width: visible ? parent.width : 0 - active: showAdvancedHeader + active: statusModal.showAdvancedHeader } Spares.StatusModalFooter { @@ -259,7 +259,7 @@ QC.Popup { id: advancedFooter anchors.bottom: parent.bottom width: visible ? parent.width : 0 - active: showAdvancedFooter + active: statusModal.showAdvancedFooter } } } diff --git a/src/StatusQ/Popups/StatusStackModal.qml b/src/StatusQ/Popups/StatusStackModal.qml new file mode 100644 index 00000000..1d3fa52d --- /dev/null +++ b/src/StatusQ/Popups/StatusStackModal.qml @@ -0,0 +1,109 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 + +StatusModal { + id: root + + /* + stackItems + Attached properties: + canGoNext + + replaceItem + Attached properties: + title + acceptButton + */ + + property string stackTitle: qsTr("StackModal") + + property alias stackItems: stackLayout.children + property alias currentIndex: stackLayout.currentIndex + property alias replaceItem: replaceLoader.sourceComponent + + readonly property int itemsCount: stackLayout.children.length + readonly property var currentItem: stackLayout.currentItem + + property Item nextButton: StatusButton { + text: qsTr("Next") + enabled: !!currentItem && (typeof(currentItem.canGoNext) == "undefined" || currentItem.canGoNext) + onClicked: currentIndex++ + } + + property Item finishButton: StatusButton { + text: qsTr("Finish") + onClicked: root.close() + } + + function replace(item) { replaceItem = item; } + + function updateRightButtons() { + if (replaceItem) { + nextButton.visible = false; + finishButton.visible = false; + } else if (currentIndex < itemsCount - 1) { + nextButton.visible = true; + finishButton.visible = false; + } else { + nextButton.visible = false; + finishButton.visible = true; + } + } + + onCurrentIndexChanged: updateRightButtons() + onReplaceItemChanged: updateRightButtons() + + width: 640 + height: Math.max(implicitHeight, replaceLoader.implicitHeight) + padding: 16 + + header.title: replaceLoader.item && typeof(replaceLoader.item.title) != "undefined" + ? replaceLoader.item.title : stackTitle + + leftButtons: StatusRoundButton { + id: backButton + icon.name: "arrow-right" + icon.width: 20 + icon.height: 16 + rotation: 180 + visible: replaceItem || stackLayout.currentIndex > 0 + onClicked: { + if (replaceItem) + replaceItem = null; + else + stackLayout.currentIndex--; + } + } + + rightButtons: [ nextButton, finishButton ] + + Item { + id: content + anchors.fill: parent + implicitWidth: stackLayout.implicitWidth + implicitHeight: stackLayout.implicitHeight + clip: true + + StatusAnimatedStack { + id: stackLayout + anchors.fill: parent + visible: !replaceItem + } + + Loader { + id: replaceLoader + anchors.fill: parent + visible: item + onItemChanged: { + root.rightButtons = item ? item.rightButtons : [ nextButton, finishButton ] + if (!item && root.itemsCount == 0) { + root.close(); + } + } + } + } +} \ No newline at end of file diff --git a/src/StatusQ/Popups/qmldir b/src/StatusQ/Popups/qmldir index 39f7a142..381befc9 100644 --- a/src/StatusQ/Popups/qmldir +++ b/src/StatusQ/Popups/qmldir @@ -6,6 +6,7 @@ StatusMenuItem 0.1 StatusMenuItem.qml StatusMenuItemDelegate 0.1 StatusMenuItemDelegate.qml StatusMenuHeadline 0.1 StatusMenuHeadline.qml StatusModal 0.1 StatusModal.qml +StatusStackModal 0.1 StatusStackModal.qml StatusSearchPopup 0.1 StatusSearchPopup.qml StatusModalDivider 0.1 StatusModalDivider.qml StatusSearchPopupMenuItem 0.1 StatusSearchPopupMenuItem.qml