diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c9bdb051e8..9f3df2e8e4 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -329,10 +329,12 @@ QML_RES_ICONS = \ qml/res/icons/cross.png \ qml/res/icons/export.png \ qml/res/icons/gear.png \ + qml/res/icons/gear-outline.png \ qml/res/icons/info.png \ qml/res/icons/network-dark.png \ qml/res/icons/network-light.png \ qml/res/icons/shutdown.png \ + qml/res/icons/singlesig-wallet.png \ qml/res/icons/storage-dark.png \ qml/res/icons/storage-light.png @@ -369,6 +371,7 @@ QML_RES_QML = \ qml/controls/PageIndicator.qml \ qml/controls/NavigationBar.qml \ qml/controls/NavigationBar2.qml \ + qml/controls/NavigationTab.qml \ qml/controls/OptionButton.qml \ qml/controls/OptionSwitch.qml \ qml/controls/OutlineButton.qml \ @@ -399,7 +402,8 @@ QML_RES_QML = \ qml/pages/settings/SettingsDisplay.qml \ qml/pages/settings/SettingsProxy.qml \ qml/pages/settings/SettingsStorage.qml \ - qml/pages/settings/SettingsTheme.qml + qml/pages/settings/SettingsTheme.qml \ + qml/pages/wallet/DesktopWallets.qml if TARGET_ANDROID BITCOIN_QT_H += qml/androidnotifier.h diff --git a/src/qml/appmode.h b/src/qml/appmode.h index 8265984103..ba15865eda 100644 --- a/src/qml/appmode.h +++ b/src/qml/appmode.h @@ -12,6 +12,7 @@ class AppMode : public QObject Q_OBJECT Q_PROPERTY(bool isDesktop READ isDesktop NOTIFY modeChanged) Q_PROPERTY(bool isMobile READ isMobile NOTIFY modeChanged) + Q_PROPERTY(bool walletEnabled READ walletEnabled NOTIFY walletEnabledChanged) Q_PROPERTY(QString state READ state NOTIFY modeChanged) public: @@ -20,12 +21,15 @@ class AppMode : public QObject MOBILE }; - explicit AppMode(Mode mode) : m_mode(mode) + explicit AppMode(Mode mode, bool wallet_enabled) + : m_mode(mode) + , m_wallet_enabled(wallet_enabled) { } bool isMobile() { return m_mode == MOBILE; } bool isDesktop() { return m_mode == DESKTOP; } + bool walletEnabled() { return m_wallet_enabled; } QString state() { switch (m_mode) { @@ -41,9 +45,11 @@ class AppMode : public QObject Q_SIGNALS: void modeChanged(); + void walletEnabledChanged(); private: const Mode m_mode; + const bool m_wallet_enabled; }; #endif // BITCOIN_QML_APPMODE_H diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index d87386cbd5..90e713bfd7 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -75,17 +75,33 @@ void SetupUIArgs(ArgsManager& argsman) argsman.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); } +AppMode SetupAppMode() +{ + bool wallet_enabled; + AppMode::Mode mode; + #ifdef __ANDROID__ + mode = AppMode::MOBILE; + #else + mode = AppMode::DESKTOP; + #endif // __ANDROID__ + + #ifdef ENABLE_WALLET + wallet_enabled = true; + #else + wallet_enabled = false; + #endif // ENABLE_WALLET + + return AppMode(mode, wallet_enabled); +} + bool InitErrorMessageBox( const bilingual_str& message, [[maybe_unused]] const std::string& caption, [[maybe_unused]] unsigned int style) { QQmlApplicationEngine engine; -#ifdef __ANDROID__ - AppMode app_mode(AppMode::MOBILE); -#else - AppMode app_mode(AppMode::DESKTOP); -#endif // __ANDROID__ + + AppMode app_mode = SetupAppMode(); qmlRegisterSingletonInstance("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode); engine.rootContext()->setContextProperty("message", QString::fromStdString(message.translated)); @@ -284,11 +300,8 @@ int QmlGuiMain(int argc, char* argv[]) engine.rootContext()->setContextProperty("optionsModel", &options_model); engine.rootContext()->setContextProperty("needOnboarding", need_onboarding); -#ifdef __ANDROID__ - AppMode app_mode(AppMode::MOBILE); -#else - AppMode app_mode(AppMode::DESKTOP); -#endif // __ANDROID__ + + AppMode app_mode = SetupAppMode(); qmlRegisterSingletonInstance("org.bitcoincore.qt", 1, 0, "AppMode", &app_mode); qmlRegisterType("org.bitcoincore.qt", 1, 0, "BlockClockDial"); diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 1c98e6dc6d..86372d3c4f 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -31,6 +31,7 @@ controls/PageIndicator.qml controls/NavigationBar.qml controls/NavigationBar2.qml + controls/NavigationTab.qml controls/OptionButton.qml controls/OptionSwitch.qml controls/OutlineButton.qml @@ -62,6 +63,7 @@ pages/settings/SettingsProxy.qml pages/settings/SettingsStorage.qml pages/settings/SettingsTheme.qml + pages/wallet/DesktopWallets.qml res/icons/arrow-down.png @@ -78,10 +80,12 @@ res/icons/cross.png res/icons/export.png res/icons/gear.png + res/icons/gear-outline.png res/icons/info.png res/icons/network-dark.png res/icons/network-light.png res/icons/shutdown.png + res/icons/singlesig-wallet.png res/icons/storage-dark.png res/icons/storage-light.png diff --git a/src/qml/components/BlockClock.qml b/src/qml/components/BlockClock.qml index a02abae6a4..6e1c28f0a7 100644 --- a/src/qml/components/BlockClock.qml +++ b/src/qml/components/BlockClock.qml @@ -15,6 +15,7 @@ Item { id: root property real parentWidth: 600 property real parentHeight: 600 + property bool showNetworkIndicator: true width: dial.width height: dial.height + networkIndicator.height + networkIndicator.anchors.topMargin @@ -146,6 +147,7 @@ Item { NetworkIndicator { id: networkIndicator + show: root.showNetworkIndicator anchors.top: dial.bottom anchors.topMargin: networkIndicator.visible ? 30 : 0 anchors.horizontalCenter: root.horizontalCenter diff --git a/src/qml/components/NetworkIndicator.qml b/src/qml/components/NetworkIndicator.qml index 471ccfbbcd..7743822e7a 100644 --- a/src/qml/components/NetworkIndicator.qml +++ b/src/qml/components/NetworkIndicator.qml @@ -12,12 +12,14 @@ import org.bitcoincore.qt 1.0 Button { id: root property color bgColor + property bool shorten: false + property bool show: true property int textSize: 15 topPadding: 2 bottomPadding: 2 leftPadding: 7 rightPadding: 7 - state: chainModel.currentNetworkName + state: show ? chainModel.currentNetworkName : "MAIN" contentItem: CoreText { text: root.text font.pixelSize: root.textSize @@ -47,7 +49,7 @@ Button { PropertyChanges { target: root visible: true - text: qsTr("Test Network") + text: shorten ? qsTr("Testnet") : qsTr("Test Network") bgColor: Theme.color.green } }, @@ -56,7 +58,7 @@ Button { PropertyChanges { target: root visible: true - text: qsTr("Signet Network") + text: shorten ? qsTr("Signet") : qsTr("Signet Network") bgColor: Theme.color.amber } }, @@ -65,7 +67,7 @@ Button { PropertyChanges { target: root visible: true - text: qsTr("Regtest Mode") + text: shorten ? qsTr("Regtest") : qsTr("Regtest Mode") bgColor: Theme.color.blue } } diff --git a/src/qml/controls/NavigationTab.qml b/src/qml/controls/NavigationTab.qml new file mode 100644 index 0000000000..219c90a135 --- /dev/null +++ b/src/qml/controls/NavigationTab.qml @@ -0,0 +1,84 @@ +// 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 org.bitcoincore.qt 1.0 + +Button { + property color bgActiveColor: Theme.color.orange + property color textColor: Theme.color.neutral7 + property color textHoverColor: Theme.color.neutral9 + property color textActiveColor: Theme.color.orange + property color iconColor: "transparent" + property string iconSource: "" + + id: root + checkable: true + hoverEnabled: AppMode.isDesktop + implicitHeight: 60 + implicitWidth: 80 + bottomPadding: 0 + topPadding: 0 + + contentItem: Item { + width: parent.width + height: parent.height + CoreText { + id: buttonText + font.pixelSize: 15 + text: root.text + color: root.textColor + bold: true + visible: root.text !== "" + anchors.centerIn: parent + } + Icon { + id: icon + source: root.iconSource + color: iconColor + visible: root.iconSource !== "" + anchors.centerIn: parent + } + } + + background: Item { + Rectangle { + id: bg + height: parent.height - 5 + width: parent.width + radius: 5 + color: Theme.color.neutral3 + visible: root.hovered + + FocusBorder { + visible: root.visualFocus + } + + Behavior on color { + ColorAnimation { duration: 150 } + } + } + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: 3 + visible: root.checked + color: root.bgActiveColor + } + } + + states: [ + State { + name: "CHECKED"; when: root.checked + PropertyChanges { target: buttonText; color: root.textActiveColor } + PropertyChanges { target: icon; color: root.textActiveColor } + }, + State { + name: "HOVER"; when: root.hovered + PropertyChanges { target: buttonText; color: root.textHoverColor } + PropertyChanges { target: icon; color: root.textHoverColor } + } + ] +} diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index 26ffc102ea..6a739a5aee 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -92,6 +92,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/gear").pixmap(requested_size); } + if (id == "gear-outline") { + *size = requested_size; + return QIcon(":/icons/gear-outline").pixmap(requested_size); + } + if (id == "info") { *size = requested_size; return QIcon(":/icons/info").pixmap(requested_size); @@ -112,6 +117,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/shutdown").pixmap(requested_size); } + if (id == "singlesig-wallet") { + *size = requested_size; + return QIcon(":/icons/singlesig-wallet").pixmap(requested_size); + } + if (id == "storage-dark") { *size = requested_size; return QIcon(":/icons/storage-dark").pixmap(requested_size); diff --git a/src/qml/pages/main.qml b/src/qml/pages/main.qml index 581e9b42b4..4c1c964d7c 100644 --- a/src/qml/pages/main.qml +++ b/src/qml/pages/main.qml @@ -11,6 +11,7 @@ import "../components" import "../controls" import "./onboarding" import "./node" +import "./wallet" ApplicationWindow { id: appWindow @@ -33,7 +34,17 @@ ApplicationWindow { StackView { id: main - initialItem: needOnboarding ? onboardingWizard : node + initialItem: { + if (needOnboarding) { + onboardingWizard + } else { + if (AppMode.walletEnabled && AppMode.isDesktop) { + desktopWallets + } else { + node + } + } + } anchors.fill: parent focus: true Keys.onReleased: { @@ -66,10 +77,21 @@ ApplicationWindow { OnboardingStorageAmount {} OnboardingConnection {} - onFinishedChanged: main.push(node) + onFinishedChanged: { + if (AppMode.walletEnabled && AppMode.isDesktop) { + main.push(desktopWallets) + } else { + main.push(node) + } + } } } + Component { + id: desktopWallets + DesktopWallets {} + } + Component { id: shutdown Shutdown {} diff --git a/src/qml/pages/node/NodeSettings.qml b/src/qml/pages/node/NodeSettings.qml index 87a5f5de4b..a2ca4c667b 100644 --- a/src/qml/pages/node/NodeSettings.qml +++ b/src/qml/pages/node/NodeSettings.qml @@ -12,6 +12,8 @@ import "../settings" Item { signal doneClicked + property alias showDoneButton: doneButton.visible + id: root StackView { @@ -33,6 +35,7 @@ Item { header: "Settings" } rightItem: NavButton { + id: doneButton text: qsTr("Done") onClicked: root.doneClicked() } diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml new file mode 100644 index 0000000000..3c5c20fc10 --- /dev/null +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -0,0 +1,124 @@ +// 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 org.bitcoincore.qt 1.0 +import "../../controls" +import "../../components" +import "../node" + +Page { + id: root + background: null + + ButtonGroup { id: navigationTabs } + + header: NavigationBar2 { + id: navBar + leftItem: RowLayout { + spacing: 5 + Icon { + source: "image://images/singlesig-wallet" + color: Theme.color.neutral8 + Layout.preferredWidth: 30 + Layout.preferredHeight: 30 + Layout.leftMargin: 10 + } + Column { + spacing: 2 + CoreText { + text: "Singlesig Wallet" + color: Theme.color.neutral7 + bold: true + } + CoreText { + text: " 0.00 167 599" + color: Theme.color.neutral7 + } + } + } + centerItem: RowLayout { + NavigationTab { + id: activityTabButton + checked: true + text: qsTr("Activity") + property int index: 0 + ButtonGroup.group: navigationTabs + } + NavigationTab { + text: qsTr("Send") + property int index: 1 + ButtonGroup.group: navigationTabs + } + NavigationTab { + text: qsTr("Receive") + property int index: 2 + ButtonGroup.group: navigationTabs + } + } + rightItem: RowLayout { + spacing: 5 + NetworkIndicator { + textSize: 11 + Layout.rightMargin: 5 + shorten: true + } + NavigationTab { + Layout.preferredWidth: 30 + Layout.rightMargin: 10 + property int index: 3 + ButtonGroup.group: navigationTabs + } + NavigationTab { + iconSource: "image://images/gear-outline" + iconColor: Theme.color.neutral7 + Layout.preferredWidth: 30 + property int index: 4 + ButtonGroup.group: navigationTabs + } + } + background: Rectangle { + color: Theme.color.neutral4 + anchors.bottom: navBar.bottom + anchors.bottomMargin: 4 + height: 1 + width: parent.width + } + } + + StackLayout { + width: parent.width + height: parent.height + currentIndex: navigationTabs.checkedButton.index + Item { + id: activityTab + CoreText { text: "Activity" } + } + Item { + id: sendTab + CoreText { text: "Send" } + } + Item { + id: receiveTab + CoreText { text: "Receive" } + } + Item { + id: blockClockTab + anchors.fill: parent + BlockClock { + parentWidth: parent.width - 40 + parentHeight: parent.height + anchors.centerIn: parent + showNetworkIndicator: false + } + } + NodeSettings { + showDoneButton: false + } + } + + Component.onCompleted: nodeModel.startNodeInitializionThread(); +} diff --git a/src/qml/res/icons/gear-outline.png b/src/qml/res/icons/gear-outline.png new file mode 100644 index 0000000000..2a868c7700 Binary files /dev/null and b/src/qml/res/icons/gear-outline.png differ diff --git a/src/qml/res/icons/singlesig-wallet.png b/src/qml/res/icons/singlesig-wallet.png new file mode 100644 index 0000000000..36a82fec8a Binary files /dev/null and b/src/qml/res/icons/singlesig-wallet.png differ diff --git a/src/qml/res/src/gear-outline.svg b/src/qml/res/src/gear-outline.svg new file mode 100644 index 0000000000..8c7e5dd018 --- /dev/null +++ b/src/qml/res/src/gear-outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/qml/res/src/singlesig-wallet.svg b/src/qml/res/src/singlesig-wallet.svg new file mode 100644 index 0000000000..76d1ca5019 --- /dev/null +++ b/src/qml/res/src/singlesig-wallet.svg @@ -0,0 +1,3 @@ + + +