From 582c6ec870573d1021733a761e57ada31c84c95b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 10 Jan 2021 12:52:44 +0100 Subject: [PATCH 01/12] merge bitcoin-core/gui#180: connection type follow-ups --- src/net.h | 11 ++++++----- src/qt/forms/debugwindow.ui | 2 +- src/qt/guiutil.cpp | 4 ++-- src/qt/guiutil.h | 2 +- src/qt/rpcconsole.cpp | 20 ++++++++++++++++---- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/net.h b/src/net.h index 77856e8bf91f..a345802d4eab 100644 --- a/src/net.h +++ b/src/net.h @@ -148,7 +148,8 @@ struct CSerializedNetMsg * connection. Aside from INBOUND, all types are initiated by us. * * If adding or removing types, please update CONNECTION_TYPE_DOC in - * src/rpc/net.cpp. */ + * src/rpc/net.cpp and src/qt/rpcconsole.cpp, as well as the descriptions in + * src/qt/guiutil.cpp and src/bitcoin-cli.cpp::NetinfoRequestHandler. */ enum class ConnectionType { /** * Inbound connections are those initiated by a peer. This is the only @@ -159,7 +160,7 @@ enum class ConnectionType { /** * These are the default connections that we use to connect with the - * network. There is no restriction on what is relayed- by default we relay + * network. There is no restriction on what is relayed; by default we relay * blocks, addresses & transactions. We automatically attempt to open * MAX_OUTBOUND_FULL_RELAY_CONNECTIONS using addresses from our AddrMan. */ @@ -167,8 +168,8 @@ enum class ConnectionType { /** - * We open manual connections to addresses that users explicitly inputted - * via the addnode RPC, or the -connect command line argument. Even if a + * We open manual connections to addresses that users explicitly requested + * via the addnode RPC or the -addnode/-connect configuration options. Even if a * manual connection is misbehaving, we do not automatically disconnect or * add it to our discouragement filter. */ @@ -187,7 +188,7 @@ enum class ConnectionType { * although in our codebase feeler connections encompass test-before-evict as well. * We make these connections approximately every FEELER_INTERVAL: * first we resolve previously found collisions if they exist (test-before-evict), - * otherwise connect to a node from the new table. + * otherwise we connect to a node from the new table. */ FEELER, diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 11a5cad0a669..80526d9bce8a 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1028,7 +1028,7 @@ - The type of peer connection:<ul><li>Inbound: initiated by peer</li><li>Outbound Full Relay: default</li><li>Outbound Block Relay: does not relay transactions or addresses</li><li>Outbound Manual: added using RPC %1 or %2/%3 configuration options</li><li>Outbound Feeler: short-lived, for testing addresses</li><li>Outbound Address Fetch: short-lived, for soliciting addresses</li></ul> + The type of peer connection: %1 Connection Type diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 967a4fb870bb..36b8d7bd5224 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1661,10 +1661,10 @@ QString NetworkToQString(Network net) assert(false); } -QString ConnectionTypeToQString(ConnectionType conn_type) +QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes) { switch (conn_type) { - case ConnectionType::INBOUND: return QObject::tr("Inbound"); + case ConnectionType::INBOUND: return relay_txes ? QObject::tr("Inbound Full Relay") : QObject::tr("Inbound Block Relay"); case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 53f1b98a558c..4af9e0961727 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -398,7 +398,7 @@ namespace GUIUtil QString NetworkToQString(Network net); /** Convert enum ConnectionType to QString */ - QString ConnectionTypeToQString(ConnectionType conn_type); + QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes); /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(std::chrono::seconds dur); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index a8ce8f8d60eb..21ee596b39ab 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -18,9 +18,10 @@ #include #include #include -#include #include +#include #include +#include #include #include #include @@ -476,11 +477,22 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags ui->splitter->restoreState(settings.value("PeersTabSplitterSizes").toByteArray()); - QChar nonbreaking_hyphen(8209); + constexpr QChar nonbreaking_hyphen(8209); + const std::vector CONNECTION_TYPE_DOC{ + tr("Inbound Full/Block Relay: initiated by peer"), + tr("Outbound Full Relay: default"), + tr("Outbound Block Relay: does not relay transactions or addresses"), + tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options") + .arg("addnode") + .arg(QString(nonbreaking_hyphen) + "addnode") + .arg(QString(nonbreaking_hyphen) + "connect"), + tr("Outbound Feeler: short-lived, for testing addresses"), + tr("Outbound Address Fetch: short-lived, for soliciting addresses")}; + const QString list{"
  • " + Join(CONNECTION_TYPE_DOC, QString("
  • ")) + "
"}; + ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg(list)); ui->dataDir->setToolTip(ui->dataDir->toolTip().arg(QString(nonbreaking_hyphen) + "datadir")); ui->blocksDir->setToolTip(ui->blocksDir->toolTip().arg(QString(nonbreaking_hyphen) + "blocksdir")); ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(PACKAGE_NAME)); - ui->peerConnectionTypeLabel->setToolTip(ui->peerConnectionTypeLabel->toolTip().arg("addnode").arg(QString(nonbreaking_hyphen) + "addnode").arg(QString(nonbreaking_hyphen) + "connect")); setButtonIcons(); @@ -1241,7 +1253,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, stats->nodeStats.fRelayTxes)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) { ui->peerPermissions->setText(tr("N/A")); From 57597df2f2094ef16024bc01d7780cfbe92dfe26 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 30 Jan 2021 18:53:40 +0100 Subject: [PATCH 02/12] merge bitcoin-core/gui#203: Display plain "Inbound" in peer details --- src/qt/guiutil.cpp | 4 ++-- src/qt/guiutil.h | 2 +- src/qt/rpcconsole.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 36b8d7bd5224..967a4fb870bb 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1661,10 +1661,10 @@ QString NetworkToQString(Network net) assert(false); } -QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes) +QString ConnectionTypeToQString(ConnectionType conn_type) { switch (conn_type) { - case ConnectionType::INBOUND: return relay_txes ? QObject::tr("Inbound Full Relay") : QObject::tr("Inbound Block Relay"); + case ConnectionType::INBOUND: return QObject::tr("Inbound"); case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 4af9e0961727..53f1b98a558c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -398,7 +398,7 @@ namespace GUIUtil QString NetworkToQString(Network net); /** Convert enum ConnectionType to QString */ - QString ConnectionTypeToQString(ConnectionType conn_type, bool relay_txes); + QString ConnectionTypeToQString(ConnectionType conn_type); /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(std::chrono::seconds dur); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 21ee596b39ab..6d17f0174251 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -479,7 +479,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags constexpr QChar nonbreaking_hyphen(8209); const std::vector CONNECTION_TYPE_DOC{ - tr("Inbound Full/Block Relay: initiated by peer"), + tr("Inbound: initiated by peer"), tr("Outbound Full Relay: default"), tr("Outbound Block Relay: does not relay transactions or addresses"), tr("Outbound Manual: added using RPC %1 or %2/%3 configuration options") @@ -1253,7 +1253,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, stats->nodeStats.fRelayTxes)); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) { ui->peerPermissions->setText(tr("N/A")); From afd0648959edcc8a9f9b2bfe84032d4b7f6ff23a Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:37:35 +0000 Subject: [PATCH 03/12] merge bitcoin-core/gui#179: Add Type column to peers window, update peer details name/tooltip --- src/qt/forms/debugwindow.ui | 4 ++-- src/qt/guiutil.cpp | 18 +++++++++++------- src/qt/guiutil.h | 2 +- src/qt/peertablemodel.cpp | 5 +++++ src/qt/peertablemodel.h | 16 ++++++++++------ src/qt/rpcconsole.cpp | 2 +- 6 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index 80526d9bce8a..6e79ad9d2c59 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -1028,10 +1028,10 @@ - The type of peer connection: %1 + The direction and type of peer connection: %1 - Connection Type + Direction/Type diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 967a4fb870bb..be8a1ef2eba1 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1661,15 +1661,19 @@ QString NetworkToQString(Network net) assert(false); } -QString ConnectionTypeToQString(ConnectionType conn_type) +QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction) { + QString prefix; + if (prepend_direction) { + prefix = (conn_type == ConnectionType::INBOUND) ? QObject::tr("Inbound") : QObject::tr("Outbound") + " "; + } switch (conn_type) { - case ConnectionType::INBOUND: return QObject::tr("Inbound"); - case ConnectionType::OUTBOUND_FULL_RELAY: return QObject::tr("Outbound Full Relay"); - case ConnectionType::BLOCK_RELAY: return QObject::tr("Outbound Block Relay"); - case ConnectionType::MANUAL: return QObject::tr("Outbound Manual"); - case ConnectionType::FEELER: return QObject::tr("Outbound Feeler"); - case ConnectionType::ADDR_FETCH: return QObject::tr("Outbound Address Fetch"); + case ConnectionType::INBOUND: return prefix; + case ConnectionType::OUTBOUND_FULL_RELAY: return prefix + QObject::tr("Full Relay"); + case ConnectionType::BLOCK_RELAY: return prefix + QObject::tr("Block Relay"); + case ConnectionType::MANUAL: return prefix + QObject::tr("Manual"); + case ConnectionType::FEELER: return prefix + QObject::tr("Feeler"); + case ConnectionType::ADDR_FETCH: return prefix + QObject::tr("Address Fetch"); } // no default case, so the compiler can warn about missing cases assert(false); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 53f1b98a558c..139c47dfc14b 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -398,7 +398,7 @@ namespace GUIUtil QString NetworkToQString(Network net); /** Convert enum ConnectionType to QString */ - QString ConnectionTypeToQString(ConnectionType conn_type); + QString ConnectionTypeToQString(ConnectionType conn_type, bool prepend_direction); /** Convert seconds into a QString with days, hours, mins, secs */ QString formatDurationStr(std::chrono::seconds dur); diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 8166e4866ee1..2eec0ac483f3 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -29,6 +29,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->m_addr_name.compare(pRight->m_addr_name) < 0; + case PeerTableModel::ConnectionType: + return pLeft->m_conn_type < pRight->m_conn_type; case PeerTableModel::Network: return pLeft->m_network < pRight->m_network; case PeerTableModel::Ping: @@ -164,6 +166,8 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const case Address: // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.m_addr_name); + case ConnectionType: + return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false); case Network: return GUIUtil::NetworkToQString(rec->nodeStats.m_network); case Ping: @@ -177,6 +181,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const } } else if (role == Qt::TextAlignmentRole) { switch (index.column()) { + case ConnectionType: case Network: return QVariant(Qt::AlignCenter); case Ping: diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 16643621f304..66228d609953 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -59,12 +59,13 @@ class PeerTableModel : public QAbstractTableModel enum ColumnIndex { NetNodeId = 0, - Address = 1, - Network = 2, - Ping = 3, - Sent = 4, - Received = 5, - Subversion = 6 + Address, + ConnectionType, + Network, + Ping, + Sent, + Received, + Subversion }; enum { @@ -94,6 +95,9 @@ public Q_SLOTS: /*: Title of Peers Table column which contains the IP/Onion/I2P address of the connected peer. */ tr("Address"), + /*: Title of Peers Table column which describes the type of + peer connection. The "type" describes why the connection exists. */ + tr("Type"), /*: Title of Peers Table column which states the network the peer connected through. */ tr("Network"), diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 6d17f0174251..4b3c1b99f38a 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1253,7 +1253,7 @@ void RPCConsole::updateDetailWidget() ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); ui->peerVersion->setText(QString::number(stats->nodeStats.nVersion)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer)); - ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type)); + ui->peerConnectionType->setText(GUIUtil::ConnectionTypeToQString(stats->nodeStats.m_conn_type, /* prepend_direction */ true)); ui->peerNetwork->setText(GUIUtil::NetworkToQString(stats->nodeStats.m_network)); if (stats->nodeStats.m_permissionFlags == NetPermissionFlags::None) { ui->peerPermissions->setText(tr("N/A")); From 722bd8d7f39f98b5df4fa4c05c20173ef87abbd7 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:42:25 +0000 Subject: [PATCH 04/12] qt: introduce GUIUtil::fixedPitchFont --- src/qt/guiutil.cpp | 5 +++++ src/qt/guiutil.h | 3 +++ src/qt/rpcconsole.cpp | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index be8a1ef2eba1..b2b024672369 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -260,6 +260,11 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } +QFont fixedPitchFont() +{ + return QFontDatabase::systemFont(QFontDatabase::FixedFont); +} + // Just some dummy data to generate a convincing random-looking (but consistent) address static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 139c47dfc14b..3fbdba6c413c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -126,6 +126,9 @@ namespace GUIUtil QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); + // Return a monospace font + QFont fixedPitchFont(); + // Set up widget for address void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI = false); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 4b3c1b99f38a..2bbdfa20df3d 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -527,7 +527,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_SETTING); updateDetailWidget(); - setFontSize(settings.value(fontSizeSettingsKey, QFontInfo(QFontDatabase::systemFont(QFontDatabase::FixedFont)).pointSize()).toInt()); + setFontSize(settings.value(fontSizeSettingsKey, QFontInfo(GUIUtil::fixedPitchFont()).pointSize()).toInt()); pageButtons = new QButtonGroup(this); pageButtons->addButton(ui->btnInfo, pageButtons->buttons().size()); @@ -892,7 +892,7 @@ void RPCConsole::clear(bool keep_prompt) ui->lineEdit->setFocus(); // Set default style sheet - ui->messagesWidget->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + ui->messagesWidget->setFont(GUIUtil::fixedPitchFont()); ui->messagesWidget->document()->setDefaultStyleSheet( QString( "table { }" From 7774bdb423eda68aa10a689f059d6adf5f11bf17 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 21 Feb 2021 18:43:34 +0200 Subject: [PATCH 05/12] partial bitcoin-core/gui#79: Embed monospaced font includes: - 89e421918ee8b9c8439317f747e5c37f0733d94b - 623de12d040af89ff7c25de6eb0a19c67179dc5f --- contrib/debian/copyright | 15 +++++++++++++++ src/Makefile.qt.include | 3 ++- src/qt/bitcoin.cpp | 2 ++ src/qt/dash.qrc | 1 + src/qt/guiutil.cpp | 5 ++++- src/qt/guiutil.h | 2 +- src/qt/res/fonts/RobotoMono-Bold.ttf | Bin 0 -> 87008 bytes 7 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/qt/res/fonts/RobotoMono-Bold.ttf diff --git a/contrib/debian/copyright b/contrib/debian/copyright index fe85494d99be..2f9c7ced90f2 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -31,6 +31,10 @@ Files: src/qt/res/icons/proxy.png Copyright: Cristian Mircea Messel License: public-domain +Files: src/qt/fonts/RobotoMono-Bold.ttf +License: Apache-2.0 +Comment: Site: https://fonts.google.com/specimen/Roboto+Mono + License: Expat Permission is hereby granted, free of charge, to any person obtaining a @@ -88,3 +92,14 @@ Comment: License: public-domain This work is in the public domain. + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index cabc199d6222..8ca112d131d9 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -352,7 +352,8 @@ RES_FONTS = \ qt/res/fonts/Montserrat/Montserrat-SemiBold.otf \ qt/res/fonts/Montserrat/Montserrat-SemiBoldItalic.otf \ qt/res/fonts/Montserrat/Montserrat-Thin.otf \ - qt/res/fonts/Montserrat/Montserrat-ThinItalic.otf + qt/res/fonts/Montserrat/Montserrat-ThinItalic.otf \ + qt/res/fonts/RobotoMono-Bold.ttf RES_ANIMATION = $(wildcard $(srcdir)/qt/res/animation/spinner-*.png) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 02d6a7991a67..1196a37dd7ad 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -576,6 +577,7 @@ int GuiMain(int argc, char* argv[]) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); BitcoinApplication app; + QFontDatabase::addApplicationFont(":/fonts/monospace"); /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these // Command-line options take precedence: diff --git a/src/qt/dash.qrc b/src/qt/dash.qrc index 8ba59831cfe5..e6cd77ad8549 100644 --- a/src/qt/dash.qrc +++ b/src/qt/dash.qrc @@ -36,6 +36,7 @@ res/css/traditional.css + res/fonts/RobotoMono-Bold.ttf res/fonts/Montserrat/Montserrat-Black.otf res/fonts/Montserrat/Montserrat-BlackItalic.otf res/fonts/Montserrat/Montserrat-Bold.otf diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index b2b024672369..d7c412ce78a0 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -260,8 +260,11 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } -QFont fixedPitchFont() +QFont fixedPitchFont(bool use_embedded_font) { + if (use_embedded_font) { + return {"Roboto Mono"}; + } return QFontDatabase::systemFont(QFontDatabase::FixedFont); } diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 3fbdba6c413c..710525a4c848 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -127,7 +127,7 @@ namespace GUIUtil QString dateTimeStr(qint64 nTime); // Return a monospace font - QFont fixedPitchFont(); + QFont fixedPitchFont(bool use_embedded_font = false); // Set up widget for address void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI = false); diff --git a/src/qt/res/fonts/RobotoMono-Bold.ttf b/src/qt/res/fonts/RobotoMono-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..900fce6848210593ee6aa630ce040a8424c23fd4 GIT binary patch literal 87008 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

41`eR`3l$Vv=zMNJq(73Y|ij<8OWC=ZbCZutR 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 MmDnSJgEZ}b0mzqS2><{9 literal 0 HcmV?d00001 From dc9b25e04c8d5bcefe86f203d4a6aeaa62cb2a5c Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:48:10 +0000 Subject: [PATCH 06/12] merge bitcoin-core/gui#219: Prevent the main window popup menu --- src/qt/bitcoingui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 089725b44ed7..08a4bec14461 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -95,6 +95,8 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle, move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); } + setContextMenuPolicy(Qt::PreventContextMenu); + #ifdef ENABLE_WALLET enableWallet = WalletModel::isWalletEnabled(); #endif // ENABLE_WALLET @@ -669,7 +671,6 @@ void BitcoinGUI::createToolBars() { QToolBar *toolbar = new QToolBar(tr("Tabs toolbar")); appToolBar = toolbar; - toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolbar->setToolButtonStyle(Qt::ToolButtonTextOnly); toolbar->setMovable(false); // remove unused icon in upper left corner From 5a67b52725e6413bcb20a47cd71d3a5d52bd2a7b Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 19 Feb 2021 01:04:18 -0500 Subject: [PATCH 07/12] merge bitcoin-core/gui#214: Disable requests context menu actions when appropriate --- src/qt/receivecoinsdialog.cpp | 17 +++++++++++++---- src/qt/receivecoinsdialog.h | 3 +++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 3a1e47ec2b50..5dcb2b66031f 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -34,9 +34,9 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(QWidget* parent) : // context menu actions QAction *copyURIAction = new QAction(tr("Copy URI"), this); QAction* copyAddressAction = new QAction(tr("Copy address"), this); - QAction *copyLabelAction = new QAction(tr("Copy label"), this); - QAction *copyMessageAction = new QAction(tr("Copy message"), this); - QAction *copyAmountAction = new QAction(tr("Copy amount"), this); + copyLabelAction = new QAction(tr("Copy label"), this); + copyMessageAction = new QAction(tr("Copy message"), this); + copyAmountAction = new QAction(tr("Copy amount"), this); // context menu contextMenu = new QMenu(this); @@ -239,9 +239,18 @@ void ReceiveCoinsDialog::copyColumnToClipboard(int column) // context menu void ReceiveCoinsDialog::showMenu(const QPoint &point) { - if (!selectedRow().isValid()) { + const QModelIndex sel = selectedRow(); + if (!sel.isValid()) { return; } + + // disable context menu actions when appropriate + const RecentRequestsTableModel* const submodel = model->getRecentRequestsTableModel(); + const RecentRequestEntry& req = submodel->entry(sel.row()); + copyLabelAction->setDisabled(req.recipient.label.isEmpty()); + copyMessageAction->setDisabled(req.recipient.message.isEmpty()); + copyAmountAction->setDisabled(req.recipient.amount == 0); + contextMenu->exec(QCursor::pos()); } diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 632c65e72426..5a33a16a1069 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -52,6 +52,9 @@ public Q_SLOTS: Ui::ReceiveCoinsDialog *ui; WalletModel *model; QMenu *contextMenu; + QAction* copyLabelAction; + QAction* copyMessageAction; + QAction* copyAmountAction; QModelIndex selectedRow(); void copyColumnToClipboard(int column); From c33afea7fdc0903ab8b688e1e94dbaed95467d59 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 25 Dec 2020 22:17:37 +0200 Subject: [PATCH 08/12] merge bitcoin-core/gui#166: Use enum type as switch argument in *TableModel --- src/qt/addresstablemodel.cpp | 44 +++++++++++++++----------------- src/qt/bantablemodel.cpp | 15 +++++------ src/qt/peertablemodel.cpp | 41 +++++++++++++++-------------- src/qt/transactiontablemodel.cpp | 30 ++++++++++++---------- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 15cc63cc5a24..258beaec70cd 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -195,42 +195,38 @@ QVariant AddressTableModel::data(const QModelIndex &index, int role) const AddressTableEntry *rec = static_cast(index.internalPointer()); - if(role == Qt::DisplayRole || role == Qt::EditRole) - { - switch(index.column()) - { + const auto column = static_cast(index.column()); + if (role == Qt::DisplayRole || role == Qt::EditRole) { + switch (column) { case Label: - if(rec->label.isEmpty() && role == Qt::DisplayRole) - { + if (rec->label.isEmpty() && role == Qt::DisplayRole) { return tr("(no label)"); - } - else - { + } else { return rec->label; } case Address: return rec->address; - } - } - else if (role == Qt::FontRole) - { - QFont font; - if(index.column() == Address) - { - font = GUIUtil::getFontNormal(); - } - return font; - } - else if (role == TypeRole) - { + } // no default case, so the compiler can warn about missing cases + assert(false); + } else if (role == Qt::FontRole) { + switch (column) { + case Label: + return QFont(); + case Address: + return GUIUtil::getFontNormal(); + } // no default case, so the compiler can warn about missing cases + assert(false); + } else if (role == TypeRole) { switch(rec->type) { case AddressTableEntry::Sending: return Send; case AddressTableEntry::Receiving: return Receive; - default: break; - } + case AddressTableEntry::Hidden: + return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); } return QVariant(); } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index a01a7bc38618..6cb4a4c54682 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -23,15 +23,13 @@ bool BannedNodeLessThan::operator()(const CCombinedBan& left, const CCombinedBan if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); - switch(column) - { + switch (static_cast(column)) { case BanTableModel::Address: return pLeft->subnet.ToString().compare(pRight->subnet.ToString()) < 0; case BanTableModel::Bantime: return pLeft->banEntry.nBanUntil < pRight->banEntry.nBanUntil; - } - - return false; + } // no default case, so the compiler can warn about missing cases + assert(false); } // private implementation @@ -119,16 +117,17 @@ QVariant BanTableModel::data(const QModelIndex &index, int role) const CCombinedBan *rec = static_cast(index.internalPointer()); + const auto column = static_cast(index.column()); if (role == Qt::DisplayRole) { - switch(index.column()) - { + switch (column) { case Address: return QString::fromStdString(rec->subnet.ToString()); case Bantime: QDateTime date = QDateTime::fromMSecsSinceEpoch(0); date = date.addSecs(rec->banEntry.nBanUntil); return QLocale::system().toString(date, QLocale::LongFormat); - } + } // no default case, so the compiler can warn about missing cases + assert(false); } return QVariant(); diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 2eec0ac483f3..8614c6ba618f 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -23,8 +23,7 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); - switch(column) - { + switch (static_cast(column)) { case PeerTableModel::NetNodeId: return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: @@ -41,9 +40,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine return pLeft->nRecvBytes < pRight->nRecvBytes; case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; - } - - return false; + } // no default case, so the compiler can warn about missing cases + assert(false); } // private implementation @@ -158,9 +156,9 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const CNodeCombinedStats *rec = static_cast(index.internalPointer()); + const auto column = static_cast(index.column()); if (role == Qt::DisplayRole) { - switch(index.column()) - { + switch (column) { case NetNodeId: return (qint64)rec->nodeStats.nodeid; case Address: @@ -178,19 +176,24 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); - } + } // no default case, so the compiler can warn about missing cases + assert(false); } else if (role == Qt::TextAlignmentRole) { - switch (index.column()) { - case ConnectionType: - case Network: - return QVariant(Qt::AlignCenter); - case Ping: - case Sent: - case Received: - return QVariant(Qt::AlignRight | Qt::AlignVCenter); - default: - return QVariant(); - } + switch (column) { + case NetNodeId: + case Address: + return {}; + case ConnectionType: + case Network: + return QVariant(Qt::AlignCenter); + case Ping: + case Sent: + case Received: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); + case Subversion: + return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); } else if (role == StatsRole) { switch (index.column()) { case NetNodeId: return QVariant::fromValue(rec); diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index fc76722a4c22..c08c7db2f92e 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -607,26 +607,29 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return QVariant(); TransactionRecord *rec = static_cast(index.internalPointer()); - switch(role) - { + const auto column = static_cast(index.column()); + switch (role) { case RawDecorationRole: - switch(index.column()) - { + switch (column) { case Status: return txStatusDecoration(rec); case Watchonly: return txWatchonlyDecoration(rec); + case Date: return {}; + case Type: return {}; case ToAddress: return txAddressDecoration(rec); - } - break; + case Amount: return {}; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::DecorationRole: { return qvariant_cast(index.data(RawDecorationRole)); } case Qt::DisplayRole: - switch(index.column()) - { + switch (column) { + case Status: return {}; + case Watchonly: return {}; case Date: return formatTxDate(rec); case Type: @@ -635,12 +638,11 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTxToAddress(rec, false); case Amount: return formatTxAmount(rec, true, BitcoinUnits::SeparatorStyle::ALWAYS); - } - break; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::EditRole: // Edit role is used for sorting, so return the unformatted values - switch(index.column()) - { + switch (column) { case Status: return QString::fromStdString(rec->status.sortKey); case Date: @@ -653,8 +655,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTxToAddress(rec, true); case Amount: return qint64(rec->credit + rec->debit); - } - break; + } // no default case, so the compiler can warn about missing cases + assert(false); case Qt::ToolTipRole: return formatTooltip(rec); case Qt::TextAlignmentRole: From 504f8344ba9893835984664d3c902a059c35dddf Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 25 Apr 2021 21:41:24 +0300 Subject: [PATCH 09/12] merge bitcoin-core/gui#296: Do not use QObject::tr plural syntax for numbers with a unit symbol --- src/qt/intro.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 3622de4ec1e8..ed522c61f4d1 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -304,12 +304,12 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable void Intro::UpdateFreeSpaceLabel() { - QString freeString = tr("%n GB of free space available", "", m_bytes_available / GB_BYTES); + QString freeString = tr("%1 GB of free space available").arg(m_bytes_available / GB_BYTES); if (m_bytes_available < m_required_space_gb * GB_BYTES) { - freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb); + freeString += " " + tr("(of %1 GB needed)").arg(m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR)); } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) { - freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb); + freeString += " " + tr("(%1 GB needed for full chain)").arg(m_required_space_gb); ui->freeSpace->setStyleSheet(GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_WARNING)); } else { ui->freeSpace->setStyleSheet(""); From a3ac6941a7f59decaa3c0f3eaaa5f6391babe936 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 21 Jan 2021 16:17:27 +0200 Subject: [PATCH 10/12] merge bitcoin-core/gui#194: Save/restore RPCConsole geometry only for window --- src/qt/rpcconsole.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 2bbdfa20df3d..12f931d253e4 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -470,13 +470,21 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags GUIUtil::disableMacFocusRect(this); QSettings settings; - if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { - // Restore failed (perhaps missing setting), center the window - move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); +#ifdef ENABLE_WALLET + if (WalletModel::isWalletEnabled()) { + // RPCConsole widget is a window. + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center()); + } + ui->splitter->restoreState(settings.value("RPCConsoleWindowPeersTabSplitterSizes").toByteArray()); + } else +#endif // ENABLE_WALLET + { + // RPCConsole is a child widget. + ui->splitter->restoreState(settings.value("RPCConsoleWidgetPeersTabSplitterSizes").toByteArray()); } - ui->splitter->restoreState(settings.value("PeersTabSplitterSizes").toByteArray()); - constexpr QChar nonbreaking_hyphen(8209); const std::vector CONNECTION_TYPE_DOC{ tr("Inbound: initiated by peer"), @@ -551,8 +559,18 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags RPCConsole::~RPCConsole() { QSettings settings; - settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); - settings.setValue("PeersTabSplitterSizes", ui->splitter->saveState()); +#ifdef ENABLE_WALLET + if (WalletModel::isWalletEnabled()) { + // RPCConsole widget is a window. + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); + settings.setValue("RPCConsoleWindowPeersTabSplitterSizes", ui->splitter->saveState()); + } else +#endif // ENABLE_WALLET + { + // RPCConsole is a child widget. + settings.setValue("RPCConsoleWidgetPeersTabSplitterSizes", ui->splitter->saveState()); + } + m_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete pageButtons; From 09a8fc1df9e58c66836807be12de44eaf1f23af2 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 14 Apr 2021 01:21:49 +0530 Subject: [PATCH 11/12] merge bitcoin-core/gui#280: Remove user input from URI error message --- src/qt/paymentserver.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 4419acd8d498..693177fd800b 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -233,14 +233,17 @@ void PaymentServer::handleURIOrFile(const QString& s) SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - if (!IsValidDestinationString(recipient.address.toStdString())) { + std::string error_msg; + const CTxDestination dest = DecodeDestination(recipient.address.toStdString(), error_msg); + + if (!IsValidDestination(dest)) { if (uri.hasQueryItem("r")) { // payment request Q_EMIT message(tr("URI handling"), tr("Cannot process payment request as BIP70 is no longer supported.")+ tr("Due to discontinued support, you should request the merchant to provide you with a BIP21 compatible URI or use a wallet that does continue to support BIP70."), CClientUIInterface::ICON_WARNING); } else { - Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), + Q_EMIT message(tr("URI handling"), QString::fromStdString(error_msg), CClientUIInterface::MSG_ERROR); } } From ddca614017fbb18386dae4c100b7532a0755f61f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 12 May 2021 21:39:48 +0300 Subject: [PATCH 12/12] merge bitcoin-core/gui#325: Align numbers in the "Peer Id" column to the right --- src/qt/peertablemodel.cpp | 1 + src/qt/rpcconsole.cpp | 23 ++++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 8614c6ba618f..5c60e25f8f9f 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -181,6 +181,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const } else if (role == Qt::TextAlignmentRole) { switch (column) { case NetNodeId: + return QVariant(Qt::AlignRight | Qt::AlignVCenter); case Address: return {}; case ConnectionType: diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 12f931d253e4..f15dce30e2cc 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -43,16 +43,18 @@ #include #include #include +#include +#include #include #include #include #include #include -#include -#include #include #include - +#include +#include +#include const int CONSOLE_HISTORY = 50; const QSize FONT_RANGE(4, 40); @@ -130,6 +132,20 @@ class QtRPCTimerInterface: public RPCTimerInterface } }; +class PeerIdViewDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit PeerIdViewDelegate(QObject* parent = nullptr) + : QStyledItemDelegate(parent) {} + + QString displayText(const QVariant& value, const QLocale& locale) const override + { + // Additional spaces should visually separate right-aligned content + // from the next column to the right. + return value.toString() + QLatin1String(" "); + } +}; #include @@ -666,6 +682,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_ ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); ui->peerWidget->horizontalHeader()->setStretchLastSection(true); + ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this)); // create peer table context menu actions QAction* disconnectAction = new QAction(tr("&Disconnect"), this);