diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 33f4e26f24..e9a0dd7f72 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -398,6 +398,7 @@ QML_RES_QML = \ qml/controls/OptionButton.qml \ qml/controls/OptionSwitch.qml \ qml/controls/OutlineButton.qml \ + qml/controls/PageStack.qml \ qml/controls/ProgressIndicator.qml \ qml/controls/qmldir \ qml/controls/Setting.qml \ @@ -420,6 +421,7 @@ QML_RES_QML = \ qml/pages/onboarding/OnboardingStorageAmount.qml \ qml/pages/onboarding/OnboardingStorageLocation.qml \ qml/pages/onboarding/OnboardingStrengthen.qml \ + qml/pages/onboarding/OnboardingWizard.qml \ qml/pages/settings/SettingsAbout.qml \ qml/pages/settings/SettingsBlockClockDisplayMode.qml \ qml/pages/settings/SettingsConnection.qml \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index e64fab65e1..61d2607fca 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -38,6 +38,7 @@ controls/OptionButton.qml controls/OptionSwitch.qml controls/OutlineButton.qml + controls/PageStack.qml controls/ProgressIndicator.qml controls/qmldir controls/Setting.qml @@ -60,6 +61,7 @@ pages/onboarding/OnboardingStorageAmount.qml pages/onboarding/OnboardingStorageLocation.qml pages/onboarding/OnboardingStrengthen.qml + pages/onboarding/OnboardingWizard.qml pages/settings/SettingsAbout.qml pages/settings/SettingsBlockClockDisplayMode.qml pages/settings/SettingsConnection.qml diff --git a/src/qml/controls/PageStack.qml b/src/qml/controls/PageStack.qml new file mode 100644 index 0000000000..e05a2b12f1 --- /dev/null +++ b/src/qml/controls/PageStack.qml @@ -0,0 +1,51 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +StackView { + property bool vertical: false + + pushEnter: Transition { + NumberAnimation { + property: vertical ? "y" : "x" + from: vertical ? parent.height : parent.width + to: 0 + duration: 400 + easing.type: Easing.Bezier + easing.bezierCurve: [0.5, 0.0, 0.2, 1.0] + } + } + pushExit: Transition { + NumberAnimation { + property: vertical ? "y" : "x" + from: 0 + to: vertical ? -parent.height : -parent.width + duration: 400 + easing.type: Easing.Bezier + easing.bezierCurve: [0.5, 0.0, 0.2, 1.0] + } + } + popEnter: Transition { + NumberAnimation { + property: vertical ? "y" : "x" + from: vertical ? -parent.height : -parent.width + to: 0 + duration: 400 + easing.type: Easing.Bezier + easing.bezierCurve: [0.5, 0.0, 0.2, 1.0] + } + } + popExit: Transition { + NumberAnimation { + property: vertical ? "y" : "x" + from: 0 + to: vertical ? parent.height : parent.width + duration: 400 + easing.type: Easing.Bezier + easing.bezierCurve: [0.5, 0.0, 0.2, 1.0] + } + } +} diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index 428e5f7527..7e0854a7c2 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -32,7 +32,7 @@ ApplicationWindow { ColorAnimation { duration: 150 } } - StackView { + PageStack { id: main initialItem: { if (needOnboarding) { @@ -65,36 +65,8 @@ ApplicationWindow { Component { id: onboardingWizard - SwipeView { - id: swipeView - property bool finished: false - interactive: false - - OnboardingCover { - onNext: swipeView.incrementCurrentIndex() - } - OnboardingStrengthen { - onBack: swipeView.decrementCurrentIndex() - onNext: swipeView.incrementCurrentIndex() - } - OnboardingBlockclock { - onBack: swipeView.decrementCurrentIndex() - onNext: swipeView.incrementCurrentIndex() - } - OnboardingStorageLocation { - onBack: swipeView.decrementCurrentIndex() - onNext: swipeView.incrementCurrentIndex() - } - OnboardingStorageAmount { - onBack: swipeView.decrementCurrentIndex() - onNext: swipeView.incrementCurrentIndex() - } - OnboardingConnection { - onBack: swipeView.decrementCurrentIndex() - onNext: swipeView.finished = true - } - - onFinishedChanged: { + OnboardingWizard { + onFinished: { optionsModel.onboard() if (AppMode.walletEnabled && AppMode.isDesktop) { main.push(desktopWallets) @@ -127,18 +99,24 @@ ApplicationWindow { Component { id: node - SwipeView { - id: node_swipe - interactive: false - orientation: Qt.Vertical - NodeRunner { - onSettingsClicked: { - node_swipe.incrementCurrentIndex() + PageStack { + id: nodeStack + vertical: true + initialItem: node + Component { + id: node + NodeRunner { + onSettingsClicked: { + nodeStack.push(nodeSettings) + } } } - NodeSettings { - onDoneClicked: { - node_swipe.decrementCurrentIndex() + Component { + id: nodeSettings + NodeSettings { + onDoneClicked: { + nodeStack.pop() + } } } } diff --git a/src/qml/pages/node/NodeSettings.qml b/src/qml/pages/node/NodeSettings.qml index 527da7dff4..b9187bedb8 100644 --- a/src/qml/pages/node/NodeSettings.qml +++ b/src/qml/pages/node/NodeSettings.qml @@ -16,7 +16,7 @@ Item { id: root - StackView { + PageStack { id: nodeSettingsView anchors.fill: parent diff --git a/src/qml/pages/onboarding/OnboardingConnection.qml b/src/qml/pages/onboarding/OnboardingConnection.qml index e4b6bb75e7..81d981c3b3 100644 --- a/src/qml/pages/onboarding/OnboardingConnection.qml +++ b/src/qml/pages/onboarding/OnboardingConnection.qml @@ -15,46 +15,52 @@ Page { signal next background: null clip: true - SwipeView { - id: connections + PageStack { + id: connectionStack anchors.fill: parent - interactive: false - orientation: Qt.Vertical - InformationPage { - navLeftDetail: NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: root.back() - } - bannerItem: Image { - Layout.topMargin: 20 - Layout.alignment: Qt.AlignCenter - source: Theme.image.storage - sourceSize.width: 200 - sourceSize.height: 200 - } - bold: true - headerText: qsTr("Starting initial download") - headerMargin: 30 - description: qsTr("The application will connect to the Bitcoin network and start downloading and verifying transactions.\n\nThis may take several hours, or even days, based on your connection.") - descriptionMargin: 10 - detailActive: true - detailTopMargin: 10 - detailItem: RowLayout { - TextButton { + vertical: true + initialItem: onboardingConnection + Component { + id: onboardingConnection + InformationPage { + navLeftDetail: NavButton { + iconSource: "image://images/caret-left" + text: qsTr("Back") + onClicked: root.back() + } + bannerItem: Image { + Layout.topMargin: 20 Layout.alignment: Qt.AlignCenter - text: qsTr("Connection settings") - onClicked: connections.incrementCurrentIndex() + source: Theme.image.storage + sourceSize.width: 200 + sourceSize.height: 200 } + bold: true + headerText: qsTr("Starting initial download") + headerMargin: 30 + description: qsTr("The application will connect to the Bitcoin network and start downloading and verifying transactions.\n\nThis may take several hours, or even days, based on your connection.") + descriptionMargin: 10 + detailActive: true + detailTopMargin: 10 + detailItem: RowLayout { + TextButton { + Layout.alignment: Qt.AlignCenter + text: qsTr("Connection settings") + onClicked: connectionStack.push(connectionSettings) + } + } + lastPage: true + buttonText: qsTr("Next") + buttonMargin: 20 + onNext: root.next() } - lastPage: true - buttonText: qsTr("Next") - buttonMargin: 20 - onNext: root.next() } - SettingsConnection { - onboarding: true - onBack: connections.decrementCurrentIndex() + Component { + id: connectionSettings + SettingsConnection { + onboarding: true + onBack: connectionStack.pop() + } } } } diff --git a/src/qml/pages/onboarding/OnboardingCover.qml b/src/qml/pages/onboarding/OnboardingCover.qml index cadff79cb8..09e58153e8 100644 --- a/src/qml/pages/onboarding/OnboardingCover.qml +++ b/src/qml/pages/onboarding/OnboardingCover.qml @@ -14,47 +14,50 @@ Page { signal next background: null clip: true - SwipeView { - id: introductions + PageStack { + id: coverStack anchors.fill: parent - interactive: false - orientation: Qt.Horizontal - InformationPage { - navRightDetail: NavButton { - iconSource: "image://images/info" - iconHeight: 24 - iconWidth: 24 - iconColor: Theme.color.neutral0 - iconBackground: Rectangle { - radius: 12 - color: Theme.color.neutral9 + initialItem: onboardingCover + Component { + id: onboardingCover + InformationPage { + navRightDetail: NavButton { + iconSource: "image://images/info" + iconHeight: 24 + iconWidth: 24 + iconColor: Theme.color.neutral0 + iconBackground: Rectangle { + radius: 12 + color: Theme.color.neutral9 + } + onClicked: coverStack.push(coverSettings) } - onClicked: { - introductions.incrementCurrentIndex() + bannerItem: Image { + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + source: "image://images/app" + // Bitcoin icon has ~11% padding + sourceSize.width: 112 + sourceSize.height: 112 } + bannerMargin: 0 + bold: true + headerText: qsTr("Bitcoin Core App") + headerSize: 36 + description: qsTr("Be part of the Bitcoin network.") + descriptionMargin: 10 + descriptionSize: 24 + subtext: qsTr("100% open-source & open-design") + buttonText: qsTr("Start") + onNext: root.next() } - bannerItem: Image { - Layout.fillWidth: true - Layout.alignment: Qt.AlignCenter - source: "image://images/app" - // Bitcoin icon has ~11% padding - sourceSize.width: 112 - sourceSize.height: 112 - } - bannerMargin: 0 - bold: true - headerText: qsTr("Bitcoin Core App") - headerSize: 36 - description: qsTr("Be part of the Bitcoin network.") - descriptionMargin: 10 - descriptionSize: 24 - subtext: qsTr("100% open-source & open-design") - buttonText: qsTr("Start") - onNext: root.next() } - SettingsAbout { - onboarding: true - onBack: introductions.decrementCurrentIndex() + Component { + id: coverSettings + SettingsAbout { + onboarding: true + onBack: coverStack.pop() + } } } } diff --git a/src/qml/pages/onboarding/OnboardingStorageAmount.qml b/src/qml/pages/onboarding/OnboardingStorageAmount.qml index e590eff4ea..300fc51276 100644 --- a/src/qml/pages/onboarding/OnboardingStorageAmount.qml +++ b/src/qml/pages/onboarding/OnboardingStorageAmount.qml @@ -13,49 +13,63 @@ Page { id: root signal back signal next + property bool customStorage: false + property int customStorageAmount background: null clip: true - SwipeView { - id: storages + PageStack { + id: stack anchors.fill: parent - interactive: false - orientation: Qt.Vertical - InformationPage { - navLeftDetail: NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: root.back() - } - bannerActive: false - bold: true - headerText: qsTr("Storage") - headerMargin: 0 - description: qsTr("Data retrieved from the Bitcoin network is stored on your device.\nYou have 500GB of storage available.") - descriptionMargin: 10 - detailActive: true - detailItem: ColumnLayout { - spacing: 0 - StorageOptions { - customStorage: advancedStorage.loadedDetailItem.customStorage - customStorageAmount: advancedStorage.loadedDetailItem.customStorageAmount - Layout.maximumWidth: 450 - Layout.alignment: Qt.AlignCenter + vertical: true + initialItem: onboardingStorageAmount + Component { + id: onboardingStorageAmount + InformationPage { + navLeftDetail: NavButton { + iconSource: "image://images/caret-left" + text: qsTr("Back") + onClicked: root.back() } - TextButton { - Layout.topMargin: 10 - Layout.alignment: Qt.AlignCenter - text: qsTr("Detailed settings") - onClicked: storages.incrementCurrentIndex() + bannerActive: false + bold: true + headerText: qsTr("Storage") + headerMargin: 0 + description: qsTr("Data retrieved from the Bitcoin network is stored on your device.\nYou have 500GB of storage available.") + descriptionMargin: 10 + detailActive: true + detailItem: ColumnLayout { + spacing: 0 + StorageOptions { + customStorage: advancedStorage.loadedDetailItem.customStorage + customStorageAmount: advancedStorage.loadedDetailItem.customStorageAmount + Layout.maximumWidth: 450 + Layout.alignment: Qt.AlignCenter + } + TextButton { + Layout.topMargin: 10 + Layout.alignment: Qt.AlignCenter + text: qsTr("Detailed settings") + onClicked: stack.push(storageAmountSettings) + } } + buttonText: qsTr("Next") + buttonMargin: 20 + onNext: root.next() } - buttonText: qsTr("Next") - buttonMargin: 20 - onNext: root.next() } - SettingsStorage { - id: advancedStorage - onboarding: true - onBack: storages.decrementCurrentIndex() + Component { + id: storageAmountSettings + SettingsStorage { + id: advancedStorage + onboarding: true + onBack: stack.pop() + onCustomStorageChanged: { + root.customStorage = advancedStorage.customStorage + } + onCustomStorageAmountChanged: { + root.customStorageAmount = advancedStorage.customStorageAmount + } + } } } } diff --git a/src/qml/pages/onboarding/OnboardingWizard.qml b/src/qml/pages/onboarding/OnboardingWizard.qml new file mode 100644 index 0000000000..43b3c5b587 --- /dev/null +++ b/src/qml/pages/onboarding/OnboardingWizard.qml @@ -0,0 +1,57 @@ +// Copyright (c) 2024 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import "../../controls" + +PageStack { + id: root + + signal finished() + initialItem: cover + + Component { + id: cover + OnboardingCover { + onNext: root.push(strengthen) + } + } + Component { + id: strengthen + OnboardingStrengthen { + onBack: root.pop() + onNext: root.push(blockclock) + } + } + Component { + id: blockclock + OnboardingBlockclock { + onBack: root.pop() + onNext: root.push(storageLocation) + } + } + Component { + id: storageLocation + OnboardingStorageLocation { + onBack: root.pop() + onNext: root.push(storageAmount) + } + } + Component { + id: storageAmount + OnboardingStorageAmount { + onBack: root.pop() + onNext: root.push(connection) + } + } + Component { + id: connection + OnboardingConnection { + onBack: root.pop() + onNext: root.finished() + } + } +} \ No newline at end of file diff --git a/src/qml/pages/settings/SettingsAbout.qml b/src/qml/pages/settings/SettingsAbout.qml index b42d3973ee..b12f122f74 100644 --- a/src/qml/pages/settings/SettingsAbout.qml +++ b/src/qml/pages/settings/SettingsAbout.qml @@ -8,70 +8,75 @@ import QtQuick.Layouts 1.15 import "../../controls" import "../../components" -Item { +Page { id: root signal back property bool onboarding: false - SwipeView { - id: aboutSwipe + background: null + PageStack { + id: stack anchors.fill: parent - interactive: false - orientation: Qt.Horizontal - InformationPage { - id: about_settings - bannerActive: false - bannerMargin: 0 - bold: true - showHeader: root.onboarding - headerText: qsTr("About") - headerMargin: 0 - description: qsTr("Bitcoin Core is an open source project.\nIf you find it useful, please contribute.\n\n This is experimental software.") - descriptionMargin: 20 - detailActive: true - detailItem: AboutOptions { - onNext: aboutSwipe.incrementCurrentIndex() - } + initialItem: aboutPage + Component { + id: aboutPage + InformationPage { + id: about_settings + bannerActive: false + bannerMargin: 0 + bold: true + showHeader: root.onboarding + headerText: qsTr("About") + headerMargin: 0 + description: qsTr("Bitcoin Core is an open source project.\nIf you find it useful, please contribute.\n\n This is experimental software.") + descriptionMargin: 20 + detailActive: true + detailItem: AboutOptions { + onNext: stack.push(developerSettings) + } - states: [ - State { - when: root.onboarding - PropertyChanges { - target: about_settings - navLeftDetail: backButton - navMiddleDetail: null + states: [ + State { + when: root.onboarding + PropertyChanges { + target: about_settings + navLeftDetail: backButton + navMiddleDetail: null + } + }, + State { + when: !root.onboarding + PropertyChanges { + target: about_settings + navLeftDetail: backButton + navMiddleDetail: header + } } - }, - State { - when: !root.onboarding - PropertyChanges { - target: about_settings - navLeftDetail: backButton - navMiddleDetail: header - } - } - ] + ] - Component { - id: backButton - NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: root.back() + Component { + id: backButton + NavButton { + iconSource: "image://images/caret-left" + text: qsTr("Back") + onClicked: root.back() + } } - } - Component { - id: header - Header { - headerBold: true - headerSize: 18 - header: qsTr("About") + Component { + id: header + Header { + headerBold: true + headerSize: 18 + header: qsTr("About") + } } } } - SettingsDeveloper { - id: about_developer - onboarding: root.onboarding - onBack: aboutSwipe.decrementCurrentIndex() + Component { + id: developerSettings + SettingsDeveloper { + onboarding: root.onboarding + onBack: stack.pop() + } } } } diff --git a/src/qml/pages/settings/SettingsConnection.qml b/src/qml/pages/settings/SettingsConnection.qml index c98b343a44..d180fa2ff2 100644 --- a/src/qml/pages/settings/SettingsConnection.qml +++ b/src/qml/pages/settings/SettingsConnection.qml @@ -8,79 +8,83 @@ import QtQuick.Layouts 1.15 import "../../controls" import "../../components" -Item { +Page { id: root signal back property bool onboarding: false - SwipeView { - id: connectionSwipe - property bool onboarding: false + background: null + PageStack { + id: stack anchors.fill: parent - interactive: false - orientation: Qt.Horizontal - InformationPage { - id: connection_settings - background: null - clip: true - bannerActive: false - bold: true - showHeader: root.onboarding - headerText: qsTr("Connection settings") - headerMargin: 0 - detailActive: true - detailItem: ConnectionSettings { - onNext: connectionSwipe.incrementCurrentIndex() - } + initialItem: connectionSettings + Component { + id: connectionSettings + InformationPage { + id: connection_settings + background: null + clip: true + bannerActive: false + bold: true + showHeader: root.onboarding + headerText: qsTr("Connection settings") + headerMargin: 0 + detailActive: true + detailItem: ConnectionSettings { + onNext: stack.push(proxySettings) + } - states: [ - State { - when: root.onboarding - PropertyChanges { - target: connection_settings - navLeftDetail: null - navMiddleDetail: null - navRightDetail: doneButton - } - }, - State { - when: !root.onboarding - PropertyChanges { - target: connection_settings - navLeftDetail: backButton - navMiddleDetail: header - navRightDetail: null + states: [ + State { + when: root.onboarding + PropertyChanges { + target: connection_settings + navLeftDetail: null + navMiddleDetail: null + navRightDetail: doneButton + } + }, + State { + when: !root.onboarding + PropertyChanges { + target: connection_settings + navLeftDetail: backButton + navMiddleDetail: header + navRightDetail: null + } } - } - ] - Component { - id: backButton - NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: root.back() + ] + + Component { + id: backButton + NavButton { + iconSource: "image://images/caret-left" + text: qsTr("Back") + onClicked: root.back() + } } - } - Component { - id: header - Header { - headerBold: true - headerSize: 18 - header: qsTr("Connection settings") + Component { + id: header + Header { + headerBold: true + headerSize: 18 + header: qsTr("Connection settings") + } } - } - Component { - id: doneButton - NavButton { - text: qsTr("Done") - onClicked: root.back() + Component { + id: doneButton + NavButton { + text: qsTr("Done") + onClicked: root.back() + } } } } - SettingsProxy { - onBack: { - connectionSwipe.decrementCurrentIndex() + Component { + id: proxySettings + SettingsProxy { + onBack: stack.pop() } } } diff --git a/src/qml/pages/settings/SettingsDisplay.qml b/src/qml/pages/settings/SettingsDisplay.qml index 2c8d543452..db093efd5c 100644 --- a/src/qml/pages/settings/SettingsDisplay.qml +++ b/src/qml/pages/settings/SettingsDisplay.qml @@ -13,7 +13,7 @@ Item { id: root - StackView { + PageStack { id: displaySettingsView anchors.fill: parent diff --git a/src/qml/pages/settings/SettingsProxy.qml b/src/qml/pages/settings/SettingsProxy.qml index 2f2d847a5b..afbed4b3f1 100644 --- a/src/qml/pages/settings/SettingsProxy.qml +++ b/src/qml/pages/settings/SettingsProxy.qml @@ -14,10 +14,6 @@ Page { id: root background: null - implicitWidth: 450 - leftPadding: 20 - rightPadding: 20 - topPadding: 30 header: NavigationBar2 { leftItem: NavButton { diff --git a/src/qml/pages/settings/SettingsStorage.qml b/src/qml/pages/settings/SettingsStorage.qml index 6dffbc49b8..2ca56742f3 100644 --- a/src/qml/pages/settings/SettingsStorage.qml +++ b/src/qml/pages/settings/SettingsStorage.qml @@ -10,6 +10,8 @@ import "../../components" InformationPage { id: root + property bool customStorage: false + property bool customStorageAmount property bool onboarding: false bannerActive: false bold: true @@ -17,7 +19,15 @@ InformationPage { headerText: qsTr("Storage settings") headerMargin: 0 detailActive: true - detailItem: StorageSettings {} + detailItem: StorageSettings { + id: storageSettings + onCustomStorageChanged: { + root.customStorage = storageSettings.customStorage + } + onCustomStorageAmountChanged: { + root.customStorageAmount = storageSettings.customStorageAmount + } + } states: [ State { when: root.onboarding diff --git a/src/qml/pages/wallet/CreateWalletWizard.qml b/src/qml/pages/wallet/CreateWalletWizard.qml index ff60b637f7..f275c9d37a 100644 --- a/src/qml/pages/wallet/CreateWalletWizard.qml +++ b/src/qml/pages/wallet/CreateWalletWizard.qml @@ -10,7 +10,7 @@ import "../../components" import "../settings" import "../wallet" -StackView { +PageStack { id: root signal finished()