mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
Merge g363 via qt_peers_directionarrow-25+knots
This commit is contained in:
commit
e5d5a5f696
@ -411,7 +411,7 @@ void BitcoinApplication::initializeResult(bool success, interfaces::BlockAndHead
|
||||
|
||||
// Log this only after AppInitMain finishes, as then logging setup is guaranteed complete
|
||||
qInfo() << "Platform customization:" << platformStyle->getName();
|
||||
clientModel = new ClientModel(node(), optionsModel);
|
||||
clientModel = new ClientModel(node(), optionsModel, *platformStyle);
|
||||
window->setClientModel(clientModel, &tip_info);
|
||||
|
||||
// If '-min' option passed, start window minimized (iconified) or minimized to tray
|
||||
|
@ -33,7 +33,7 @@
|
||||
static SteadyClock::time_point g_last_header_tip_update_notification{};
|
||||
static SteadyClock::time_point g_last_block_tip_update_notification{};
|
||||
|
||||
ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QObject *parent) :
|
||||
ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, const PlatformStyle& platform_style, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_node(node),
|
||||
optionsModel(_optionsModel),
|
||||
@ -42,7 +42,7 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
|
||||
cachedBestHeaderHeight = -1;
|
||||
cachedBestHeaderTime = -1;
|
||||
|
||||
peerTableModel = new PeerTableModel(m_node, this);
|
||||
peerTableModel = new PeerTableModel(m_node, platform_style, this);
|
||||
m_peer_table_sort_proxy = new PeerTableSortProxy(this);
|
||||
m_peer_table_sort_proxy->setSourceModel(peerTableModel);
|
||||
|
||||
|
@ -20,6 +20,7 @@ class CBlockIndex;
|
||||
class OptionsModel;
|
||||
class PeerTableModel;
|
||||
class PeerTableSortProxy;
|
||||
class PlatformStyle;
|
||||
enum class SynchronizationState;
|
||||
struct LocalServiceInfo;
|
||||
|
||||
@ -58,7 +59,7 @@ class ClientModel : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, QObject *parent = nullptr);
|
||||
explicit ClientModel(interfaces::Node& node, OptionsModel *optionsModel, const PlatformStyle&, QObject *parent = nullptr);
|
||||
~ClientModel();
|
||||
|
||||
void stop();
|
||||
|
@ -6,29 +6,144 @@
|
||||
|
||||
#include <qt/guiconstants.h>
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/platformstyle.h>
|
||||
|
||||
#include <interfaces/node.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QFontInfo>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
|
||||
PeerTableModel::PeerTableModel(interfaces::Node& node, QObject* parent)
|
||||
PeerTableModel::PeerTableModel(interfaces::Node& node, const PlatformStyle& platform_style, QObject* parent)
|
||||
: QAbstractTableModel(parent),
|
||||
m_node(node)
|
||||
m_node(node),
|
||||
m_platform_style(platform_style)
|
||||
{
|
||||
// set up timer for auto refresh
|
||||
timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
|
||||
timer->setInterval(MODEL_UPDATE_DELAY);
|
||||
|
||||
DrawIcons();
|
||||
|
||||
// load initial data
|
||||
refresh();
|
||||
}
|
||||
|
||||
PeerTableModel::~PeerTableModel() = default;
|
||||
|
||||
void PeerTableModel::DrawIcons()
|
||||
{
|
||||
static constexpr auto SIZE = 32;
|
||||
static constexpr auto ARROW_HEIGHT = SIZE * 2 / 3;
|
||||
QImage icon_in(SIZE, SIZE, QImage::Format_Alpha8);
|
||||
icon_in.fill(Qt::transparent);
|
||||
QImage icon_out(icon_in);
|
||||
QPainter icon_in_painter(&icon_in);
|
||||
QPainter icon_out_painter(&icon_out);
|
||||
|
||||
// Arrow
|
||||
auto DrawArrow = [](const int x, QPainter& icon_painter) {
|
||||
icon_painter.setBrush(Qt::SolidPattern);
|
||||
QPoint shape[] = {
|
||||
{x, ARROW_HEIGHT / 2},
|
||||
{(SIZE-1) - x, 0},
|
||||
{(SIZE-1) - x, ARROW_HEIGHT-1},
|
||||
};
|
||||
icon_painter.drawConvexPolygon(shape, 3);
|
||||
};
|
||||
DrawArrow(0, icon_in_painter);
|
||||
DrawArrow(SIZE-1, icon_out_painter);
|
||||
|
||||
{
|
||||
//: Label on inbound connection icon
|
||||
const QString label_in = tr("IN");
|
||||
//: Label on outbound connection icon
|
||||
const QString label_out = tr("OUT");
|
||||
QImage scratch(SIZE, SIZE, QImage::Format_Alpha8);
|
||||
QPainter scratch_painter(&scratch);
|
||||
QFont font; // NOTE: Application default font
|
||||
font.setBold(true);
|
||||
auto CheckSize = [&](const QImage& icon, const QString& text, const bool align_right) {
|
||||
// Make sure it's at least able to fit (width only)
|
||||
if (scratch_painter.boundingRect(0, 0, SIZE, SIZE, 0, text).width() > SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Draw text on the scratch image
|
||||
// NOTE: QImage::fill doesn't like QPainter being active
|
||||
scratch_painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
scratch_painter.fillRect(0, 0, SIZE, SIZE, Qt::transparent);
|
||||
scratch_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
scratch_painter.drawText(0, SIZE, text);
|
||||
|
||||
int text_offset_x = 0;
|
||||
if (align_right) {
|
||||
// Figure out how far right we can shift it
|
||||
for (int col = SIZE-1; col >= 0; --col) {
|
||||
bool any_pixels = false;
|
||||
for (int row = SIZE-1; row >= 0; --row) {
|
||||
int opacity = qAlpha(scratch.pixel(col, row));
|
||||
if (opacity > 0) {
|
||||
any_pixels = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (any_pixels) {
|
||||
text_offset_x = (SIZE-1) - col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's any overlap
|
||||
for (int row = 0; row < SIZE; ++row) {
|
||||
for (int col = text_offset_x; col < SIZE; ++col) {
|
||||
int opacity = qAlpha(icon.pixel(col, row));
|
||||
if (col >= text_offset_x) {
|
||||
opacity += qAlpha(scratch.pixel(col - text_offset_x, row));
|
||||
}
|
||||
if (opacity > 0xff) {
|
||||
// Overlap found, we're done
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
int font_size = SIZE;
|
||||
while (font_size > 1) {
|
||||
font.setPixelSize(--font_size);
|
||||
scratch_painter.setFont(font);
|
||||
if (CheckSize(icon_in , label_in , /* align_right= */ false) &&
|
||||
CheckSize(icon_out, label_out, /* align_right= */ true)) break;
|
||||
}
|
||||
icon_in_painter .drawText(0, 0, SIZE, SIZE, Qt::AlignLeft | Qt::AlignBottom, label_in);
|
||||
icon_out_painter.drawText(0, 0, SIZE, SIZE, Qt::AlignRight | Qt::AlignBottom, label_out);
|
||||
}
|
||||
m_icon_conn_in = m_platform_style.TextColorIcon(QIcon(QPixmap::fromImage(icon_in)));
|
||||
m_icon_conn_out = m_platform_style.TextColorIcon(QIcon(QPixmap::fromImage(icon_out)));
|
||||
}
|
||||
|
||||
void PeerTableModel::updatePalette()
|
||||
{
|
||||
m_icon_conn_in = m_platform_style.TextColorIcon(m_icon_conn_in);
|
||||
m_icon_conn_out = m_platform_style.TextColorIcon(m_icon_conn_out);
|
||||
if (m_peers_data.empty()) return;
|
||||
Q_EMIT dataChanged(
|
||||
createIndex(0, Direction),
|
||||
createIndex(m_peers_data.size() - 1, Direction),
|
||||
QVector<int>{Qt::DecorationRole}
|
||||
);
|
||||
}
|
||||
|
||||
void PeerTableModel::startAutoRefresh()
|
||||
{
|
||||
timer->start();
|
||||
@ -72,11 +187,7 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
|
||||
case Address:
|
||||
return QString::fromStdString(rec->nodeStats.m_addr_name);
|
||||
case Direction:
|
||||
return QString(rec->nodeStats.fInbound ?
|
||||
//: An Inbound Connection from a Peer.
|
||||
tr("Inbound") :
|
||||
//: An Outbound Connection to a Peer.
|
||||
tr("Outbound"));
|
||||
return {};
|
||||
case ConnectionType:
|
||||
return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /*prepend_direction=*/false);
|
||||
case Network:
|
||||
@ -95,10 +206,10 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
|
||||
switch (column) {
|
||||
case NetNodeId:
|
||||
case Age:
|
||||
case Direction:
|
||||
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
|
||||
case Address:
|
||||
return {};
|
||||
case Direction:
|
||||
case ConnectionType:
|
||||
case Network:
|
||||
return QVariant(Qt::AlignCenter);
|
||||
@ -112,6 +223,8 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
|
||||
assert(false);
|
||||
} else if (role == StatsRole) {
|
||||
return QVariant::fromValue(rec);
|
||||
} else if (index.column() == Direction && role == Qt::DecorationRole) {
|
||||
return rec->nodeStats.fInbound ? m_icon_conn_in : m_icon_conn_out;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
|
@ -9,12 +9,14 @@
|
||||
#include <net.h>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QIcon>
|
||||
#include <QList>
|
||||
#include <QModelIndex>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class PeerTablePriv;
|
||||
class PlatformStyle;
|
||||
|
||||
namespace interfaces {
|
||||
class Node;
|
||||
@ -40,16 +42,17 @@ class PeerTableModel : public QAbstractTableModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PeerTableModel(interfaces::Node& node, QObject* parent);
|
||||
explicit PeerTableModel(interfaces::Node& node, const PlatformStyle&, QObject* parent);
|
||||
~PeerTableModel();
|
||||
void startAutoRefresh();
|
||||
void stopAutoRefresh();
|
||||
|
||||
// See also RPCConsole::ColumnWidths in rpcconsole.h
|
||||
enum ColumnIndex {
|
||||
NetNodeId = 0,
|
||||
Age,
|
||||
Address,
|
||||
Direction,
|
||||
Address,
|
||||
ConnectionType,
|
||||
Network,
|
||||
Ping,
|
||||
@ -74,24 +77,26 @@ public:
|
||||
|
||||
public Q_SLOTS:
|
||||
void refresh();
|
||||
void updatePalette();
|
||||
|
||||
private:
|
||||
//! Internal peer data structure.
|
||||
QList<CNodeCombinedStats> m_peers_data{};
|
||||
interfaces::Node& m_node;
|
||||
const PlatformStyle& m_platform_style;
|
||||
void DrawIcons();
|
||||
QIcon m_icon_conn_in, m_icon_conn_out;
|
||||
const QStringList columns{
|
||||
/*: Title of Peers Table column which contains a
|
||||
unique number used to identify a connection. */
|
||||
tr("Peer"),
|
||||
tr("id"),
|
||||
/*: Title of Peers Table column which indicates the duration (length of time)
|
||||
since the peer connection started. */
|
||||
tr("Age"),
|
||||
"", // Direction column has no title
|
||||
/*: Title of Peers Table column which contains the
|
||||
IP/Onion/I2P address of the connected peer. */
|
||||
tr("Address"),
|
||||
/*: Title of Peers Table column which indicates the direction
|
||||
the peer connection was initiated from. */
|
||||
tr("Direction"),
|
||||
/*: Title of Peers Table column which describes the type of
|
||||
peer connection. The "type" describes why the connection exists. */
|
||||
tr("Type"),
|
||||
@ -106,7 +111,7 @@ private:
|
||||
tr("Sent"),
|
||||
/*: Title of Peers Table column which indicates the total amount of
|
||||
network information we have received from the peer. */
|
||||
tr("Received"),
|
||||
tr("Recv'd"),
|
||||
/*: Title of Peers Table column which contains the peer's
|
||||
User Agent string. */
|
||||
tr("User Agent")};
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDateTime>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QKeyEvent>
|
||||
#include <QKeySequence>
|
||||
#include <QLatin1String>
|
||||
@ -687,6 +688,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
|
||||
connect(model, &ClientModel::mempoolSizeChanged, this, &RPCConsole::setMempoolSize);
|
||||
|
||||
// set up peer table
|
||||
clientModel->getPeerTableModel()->updatePalette();
|
||||
ui->peerWidget->setModel(model->peerTableSortProxy());
|
||||
ui->peerWidget->verticalHeader()->hide();
|
||||
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
@ -694,11 +696,19 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
|
||||
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
|
||||
const QFontMetrics fm = ui->peerWidget->fontMetrics();
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::NetNodeId, GUIUtil::TextWidth(fm, QStringLiteral("99999")));
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Age, GUIUtil::TextWidth(fm, GUIUtil::FormatPeerAge(std::chrono::hours{23976 /* 999 days */})));
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Direction, DIRECTION_COLUMN_WIDTH);
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::ConnectionType, GUIUtil::TextWidth(fm, GUIUtil::ConnectionTypeToQString(ConnectionType::ADDR_FETCH /* TODO: Find the WIDEST string? */, /*prepend_direction=*/false)));
|
||||
const auto bytesize_width = GUIUtil::TextWidth(fm, GUIUtil::formatBytes(999'000'000'000) + QStringLiteral("x"));
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Network, GUIUtil::TextWidth(fm, qvariant_cast<QString>(model->peerTableSortProxy()->headerData(PeerTableModel::ColumnIndex::Network, Qt::Horizontal, Qt::DisplayRole)) /* TODO: Find the WIDEST string? */ + QStringLiteral("x")));
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Sent, bytesize_width);
|
||||
ui->peerWidget->setColumnWidth(PeerTableModel::Received, bytesize_width);
|
||||
}
|
||||
ui->peerWidget->horizontalHeader()->setSectionResizeMode(PeerTableModel::Age, QHeaderView::ResizeToContents);
|
||||
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
|
||||
ui->peerWidget->setItemDelegateForColumn(PeerTableModel::NetNodeId, new PeerIdViewDelegate(this));
|
||||
|
||||
@ -734,7 +744,6 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
|
||||
ui->banlistWidget->setColumnWidth(BanTableModel::Address, BANSUBNET_COLUMN_WIDTH);
|
||||
ui->banlistWidget->setColumnWidth(BanTableModel::Bantime, BANTIME_COLUMN_WIDTH);
|
||||
}
|
||||
ui->banlistWidget->horizontalHeader()->setSectionResizeMode(BanTableModel::Address, QHeaderView::ResizeToContents);
|
||||
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
// create ban table context menu
|
||||
@ -948,6 +957,10 @@ void RPCConsole::changeEvent(QEvent* e)
|
||||
QUrl(ICON_MAPPING[i].url),
|
||||
platformStyle->SingleColorImage(ICON_MAPPING[i].source).scaled(QSize(consoleFontSize * 2, consoleFontSize * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||
}
|
||||
|
||||
if (clientModel && clientModel->getPeerTableModel()) {
|
||||
clientModel->getPeerTableModel()->updatePalette();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget::changeEvent(e);
|
||||
|
@ -152,6 +152,7 @@ private:
|
||||
|
||||
enum ColumnWidths
|
||||
{
|
||||
DIRECTION_COLUMN_WIDTH = 32,
|
||||
ADDRESS_COLUMN_WIDTH = 200,
|
||||
SUBVERSION_COLUMN_WIDTH = 150,
|
||||
PING_COLUMN_WIDTH = 80,
|
||||
|
@ -130,7 +130,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
|
||||
OptionsModel optionsModel(node);
|
||||
bilingual_str error;
|
||||
QVERIFY(optionsModel.Init(error));
|
||||
ClientModel clientModel(node, &optionsModel);
|
||||
ClientModel clientModel(node, &optionsModel, *platformStyle);
|
||||
WalletContext& context = *node.walletLoader().context();
|
||||
AddWallet(context, wallet);
|
||||
WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get());
|
||||
|
@ -268,7 +268,7 @@ public:
|
||||
MiniGUI(interfaces::Node& node, const PlatformStyle* platformStyle) : sendCoinsDialog(platformStyle), transactionView(platformStyle), optionsModel(node) {
|
||||
bilingual_str error;
|
||||
QVERIFY(optionsModel.Init(error));
|
||||
clientModel = std::make_unique<ClientModel>(node, &optionsModel);
|
||||
clientModel = std::make_unique<ClientModel>(node, &optionsModel, *platformStyle);
|
||||
}
|
||||
|
||||
void initModelForWallet(interfaces::Node& node, const std::shared_ptr<CWallet>& wallet, const PlatformStyle* platformStyle)
|
||||
|
Loading…
Reference in New Issue
Block a user