Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions src/qt/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,23 @@
#include <chrono>
#include <memory>

#ifdef Q_OS_MACOS
#include <QAbstractSpinBox>
#endif
#include <QApplication>
#include <QDebug>
#include <QEvent>
#include <QLatin1String>
#include <QLibraryInfo>
#ifdef Q_OS_MACOS
#include <QLineEdit>
#endif
#include <QLocale>
#include <QMessageBox>
#include <QObject>
#ifdef Q_OS_MACOS
#include <QOperatingSystemVersion>
#endif
#include <QSettings>
#include <QThread>
#include <QTimer>
Expand All @@ -72,6 +83,99 @@ Q_DECLARE_METATYPE(wallet::AddressPurpose)

using util::MakeUnorderedList;

#ifdef Q_OS_MACOS
namespace {
constexpr auto MACOS_26_TEXT_FIELD_PROPERTY{"macos26RoundedTextField"};
constexpr auto MACOS_26_TEXT_FIELD_STYLE{R"(
QLineEdit[macos26RoundedTextField="true"] {
background-color: palette(base);
border: 1px solid palette(mid);
border-radius: 7px;
padding: 2px 7px;
}

QLineEdit[macos26RoundedTextField="true"]:focus {
border-color: palette(highlight);
}

QAbstractSpinBox[macos26RoundedTextField="true"] {
background-color: palette(base);
border: 1px solid palette(mid);
border-radius: 7px;
padding: 2px 18px 2px 7px;
}

QAbstractSpinBox[macos26RoundedTextField="true"]:focus {
border-color: palette(highlight);
}

QAbstractSpinBox[macos26RoundedTextField="true"]::up-button,
QAbstractSpinBox[macos26RoundedTextField="true"]::down-button {
subcontrol-origin: border;
width: 18px;
border: 0;
background-color: transparent;
}

QAbstractSpinBox[macos26RoundedTextField="true"]::up-button {
subcontrol-position: top right;
border-top-right-radius: 7px;
}

QAbstractSpinBox[macos26RoundedTextField="true"]::down-button {
subcontrol-position: bottom right;
border-bottom-right-radius: 7px;
}

QAbstractSpinBox[macos26RoundedTextField="true"]::up-button:hover,
QAbstractSpinBox[macos26RoundedTextField="true"]::down-button:hover {
background-color: palette(midlight);
}

QAbstractSpinBox[macos26RoundedTextField="true"]::up-button:pressed,
QAbstractSpinBox[macos26RoundedTextField="true"]::down-button:pressed {
background-color: palette(mid);
}

QAbstractSpinBox[macos26RoundedTextField="true"]::up-arrow {
image: url(:/icons/spin_up);
width: 8px;
height: 5px;
}

QAbstractSpinBox[macos26RoundedTextField="true"]::down-arrow {
image: url(:/icons/spin_down);
width: 8px;
height: 5px;
}

)"};

class MacOS26TextFieldStyleFilter final : public QObject
{
public:
using QObject::QObject;

protected:
bool eventFilter(QObject* object, QEvent* event) override
{
if (event->type() == QEvent::Polish) {
if (auto* line_edit = qobject_cast<QLineEdit*>(object)) {
line_edit->setAttribute(Qt::WA_MacShowFocusRect, false);
if (!qobject_cast<QAbstractSpinBox*>(line_edit->parentWidget())) {
line_edit->setProperty(MACOS_26_TEXT_FIELD_PROPERTY, true);
}
} else if (auto* spin_box = qobject_cast<QAbstractSpinBox*>(object)) {
spin_box->setAttribute(Qt::WA_MacShowFocusRect, false);
spin_box->setProperty(MACOS_26_TEXT_FIELD_PROPERTY, true);
}
}
return QObject::eventFilter(object, event);
}
};
} // namespace
#endif

static void RegisterMetaTypes()
{
// Register meta types used for QMetaObject::invokeMethod and Qt::QueuedConnection
Expand Down Expand Up @@ -203,6 +307,13 @@ BitcoinApplication::BitcoinApplication()
// Qt runs setlocale(LC_ALL, "") on initialization.
RegisterMetaTypes();
setQuitOnLastWindowClosed(false);
#ifdef Q_OS_MACOS
const QOperatingSystemVersion macos_26{QOperatingSystemVersion::MacOS, 26};
if (QOperatingSystemVersion::current() >= macos_26) {
installEventFilter(new MacOS26TextFieldStyleFilter(this));
setStyleSheet(QString::fromLatin1(MACOS_26_TEXT_FIELD_STYLE) + styleSheet());
}
#endif
}

void BitcoinApplication::setupPlatformStyle()
Expand Down
4 changes: 4 additions & 0 deletions src/qt/bitcoin.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
<file alias="fontbigger">res/icons/fontbigger.png</file>
<file alias="fontsmaller">res/icons/fontsmaller.png</file>
<file alias="prompticon">res/icons/chevron.png</file>
<file alias="spin_up">res/icons/spin-up.png</file>
<file alias="spin_up@2x">res/icons/spin-up@2x.png</file>
<file alias="spin_down">res/icons/spin-down.png</file>
<file alias="spin_down@2x">res/icons/spin-down@2x.png</file>
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
<file alias="hd_enabled">res/icons/hd_enabled.png</file>
<file alias="hd_disabled">res/icons/hd_disabled.png</file>
Expand Down
14 changes: 14 additions & 0 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#ifdef Q_OS_MACOS
#include <QOperatingSystemVersion>
#endif
#include <QProgressDialog>
#include <QScreen>
#include <QSettings>
Expand Down Expand Up @@ -148,6 +151,17 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
// Create the toolbars
createToolBars();

#ifdef Q_OS_MACOS
const QOperatingSystemVersion macos_26{QOperatingSystemVersion::MacOS, 26};
if (QOperatingSystemVersion::current() >= macos_26) {
setUnifiedTitleAndToolBarOnMac(true);
if (appToolBar) {
appToolBar->setFloatable(false);
appToolBar->setAllowedAreas(Qt::TopToolBarArea);
}
}
#endif

// Create system tray icon and notification
if (QSystemTrayIcon::isSystemTrayAvailable()) {
createTrayIcon();
Expand Down
Binary file added src/qt/res/icons/spin-down.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/qt/res/icons/spin-down@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/qt/res/icons/spin-up.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/qt/res/icons/spin-up@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/qt/res/src/spin-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/qt/res/src/spin-up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 41 additions & 1 deletion src/qt/rpcconsole.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#include <QLocale>
#include <QMenu>
#include <QMessageBox>
#ifdef Q_OS_MACOS
#include <QOperatingSystemVersion>
#include <QTabBar>
#endif
#include <QScreen>
#include <QScrollBar>
#include <QSettings>
Expand Down Expand Up @@ -71,6 +75,35 @@ const struct {

namespace {

#ifdef Q_OS_MACOS
constexpr auto MACOS_26_TAB_STYLE{R"(
QTabBar {
background-color: palette(button);
border: 1px solid palette(mid);
border-radius: 7px;
}

QTabBar::tab {
background-color: transparent;
color: palette(button-text);
border: 0;
margin: 0;
padding: 3px 12px;
}

QTabBar::tab:hover {
background-color: palette(midlight);
border-radius: 6px;
}

QTabBar::tab:selected {
background-color: palette(highlight);
color: palette(highlighted-text);
border-radius: 6px;
}
)"};
#endif

// don't add private key handling cmd's to the history
const QStringList historyFilter = QStringList()
<< "createwallet"
Expand All @@ -82,7 +115,7 @@ const QStringList historyFilter = QStringList()
<< "walletpassphrasechange"
<< "encryptwallet";

}
} // namespace

/* Object for executing console RPC commands in a separate thread.
*/
Expand Down Expand Up @@ -447,6 +480,13 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty
platformStyle(_platformStyle)
{
ui->setupUi(this);
#ifdef Q_OS_MACOS
const QOperatingSystemVersion macos_26{QOperatingSystemVersion::MacOS, 26};
if (QOperatingSystemVersion::current() >= macos_26) {
QTabBar* const tab_bar = ui->tabWidget->tabBar();
tab_bar->setStyleSheet(QString::fromLatin1(MACOS_26_TAB_STYLE));
}
#endif
QSettings settings;
#ifdef ENABLE_WALLET
if (WalletModel::isWalletEnabled()) {
Expand Down
52 changes: 52 additions & 0 deletions src/qt/test/apptests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bitcoin-build-config.h> // IWYU pragma: keep

#include <qt/test/apptests.h>

#include <chainparams.h>
Expand All @@ -15,7 +17,15 @@
#include <validation.h>

#include <QAction>
#include <QImageReader>
#include <QLineEdit>
#ifdef Q_OS_MACOS
#include <QOperatingSystemVersion>
#include <QSpinBox>
#ifdef ENABLE_WALLET
#include <QToolBar>
#endif
#endif
#include <QRegularExpression>
#include <QScopedPointer>
#include <QSignalSpy>
Expand All @@ -34,11 +44,40 @@ QString FindInConsole(const QString& output, const QString& pattern)
return re.match(output).captured(1);
}

void CheckPngResource(const QString& path, const QSize& pixel_size, qreal device_pixel_ratio)
{
QImageReader reader{path, QByteArrayLiteral("png")};
reader.setAutoDetectImageFormat(false);
QVERIFY(reader.canRead());
const QImage image{reader.read()};
QVERIFY(!image.isNull());
QCOMPARE(image.size(), pixel_size);
QCOMPARE(image.devicePixelRatio(), device_pixel_ratio);
QVERIFY(image.hasAlphaChannel());
}

//! Call getblockchaininfo RPC and check first field of JSON output.
void TestRpcCommand(RPCConsole* console)
{
QTextEdit* messagesWidget = console->findChild<QTextEdit*>("messagesWidget");
QLineEdit* lineEdit = console->findChild<QLineEdit*>("lineEdit");

CheckPngResource(QStringLiteral(":/icons/spin_up"), QSize(8, 5), 1.0);
CheckPngResource(QStringLiteral(":/icons/spin_up@2x"), QSize(16, 10), 2.0);
CheckPngResource(QStringLiteral(":/icons/spin_down"), QSize(8, 5), 1.0);
CheckPngResource(QStringLiteral(":/icons/spin_down@2x"), QSize(16, 10), 2.0);
#ifdef Q_OS_MACOS
const QOperatingSystemVersion macos_26{QOperatingSystemVersion::MacOS, 26};
if (QOperatingSystemVersion::current() >= macos_26) {
QVERIFY(!lineEdit->testAttribute(Qt::WA_MacShowFocusRect));
QVERIFY(lineEdit->property("macos26RoundedTextField").toBool());

QSpinBox spin_box;
spin_box.ensurePolished();
QVERIFY(!spin_box.testAttribute(Qt::WA_MacShowFocusRect));
QVERIFY(spin_box.property("macos26RoundedTextField").toBool());
}
#endif
QSignalSpy mw_spy(messagesWidget, &QTextEdit::textChanged);
QVERIFY(mw_spy.isValid());
QTest::keyClicks(lineEdit, "getblockchaininfo");
Expand Down Expand Up @@ -76,6 +115,19 @@ void AppTests::appTests()
void AppTests::guiTests(BitcoinGUI* window)
{
HandleCallback callback{"guiTests", *this};
#ifdef Q_OS_MACOS
const QOperatingSystemVersion macos_26{QOperatingSystemVersion::MacOS, 26};
const bool expects_unified_toolbar{QOperatingSystemVersion::current() >= macos_26};
QCOMPARE(window->unifiedTitleAndToolBarOnMac(), expects_unified_toolbar);
#ifdef ENABLE_WALLET
if (expects_unified_toolbar) {
QToolBar* const toolbar{window->findChild<QToolBar*>()};
QVERIFY(toolbar);
QVERIFY(!toolbar->isFloatable());
QCOMPARE(toolbar->allowedAreas(), Qt::TopToolBarArea);
}
#endif
#endif
connect(window, &BitcoinGUI::consoleShown, this, &AppTests::consoleTests);
expectCallback("consoleTests");
QAction* action = window->findChild<QAction*>("openRPCConsoleAction");
Expand Down
Loading