Skip to content

Commit 85cbda7

Browse files
committed
GUI: Peers: Add direction table column before "Address" with arrow
Arrow icon drawn at startup to make text translatable
1 parent 6035e9a commit 85cbda7

File tree

4 files changed

+113
-10
lines changed

4 files changed

+113
-10
lines changed

src/qt/peertablemodel.cpp

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212

1313
#include <utility>
1414

15+
#include <QBrush>
16+
#include <QFont>
17+
#include <QFontInfo>
18+
#include <QImage>
19+
#include <QPainter>
20+
#include <QPixmap>
1521
#include <QList>
1622
#include <QTimer>
1723

@@ -26,6 +32,8 @@ PeerTableModel::PeerTableModel(interfaces::Node& node, const PlatformStyle& plat
2632
connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh);
2733
timer->setInterval(MODEL_UPDATE_DELAY);
2834

35+
DrawIcons();
36+
2937
// load initial data
3038
refresh();
3139
}
@@ -35,6 +43,99 @@ PeerTableModel::~PeerTableModel()
3543
// Intentionally left empty
3644
}
3745

46+
void PeerTableModel::DrawIcons()
47+
{
48+
static constexpr auto SIZE = 32;
49+
static constexpr auto ARROW_HEIGHT = SIZE * 2 / 3;
50+
QImage icon_in(SIZE, SIZE, QImage::Format_Alpha8);
51+
icon_in.fill(Qt::transparent);
52+
QImage icon_out(icon_in);
53+
QPainter icon_in_painter(&icon_in);
54+
QPainter icon_out_painter(&icon_out);
55+
56+
// Arrow
57+
auto DrawArrow = [](const int x, QPainter& icon_painter) {
58+
icon_painter.setBrush(Qt::SolidPattern);
59+
QPoint shape[] = {
60+
{x, ARROW_HEIGHT / 2},
61+
{(SIZE-1) - x, 0},
62+
{(SIZE-1) - x, ARROW_HEIGHT-1},
63+
};
64+
icon_painter.drawConvexPolygon(shape, 3);
65+
};
66+
DrawArrow(0, icon_in_painter);
67+
DrawArrow(SIZE-1, icon_out_painter);
68+
69+
{
70+
//: Label on inbound connection icon
71+
const QString label_in = tr("IN");
72+
//: Label on outbound connection icon
73+
const QString label_out = tr("OUT");
74+
QImage scratch(SIZE, SIZE, QImage::Format_Alpha8);
75+
QPainter scratch_painter(&scratch);
76+
QFont font; // NOTE: Application default font
77+
font.setBold(true);
78+
auto CheckSize = [&](const QImage& icon, const QString& text, const bool align_right) {
79+
// Make sure it's at least able to fit (width only)
80+
if (scratch_painter.boundingRect(0, 0, SIZE, SIZE, 0, text).width() > SIZE) {
81+
return false;
82+
}
83+
84+
// Draw text on the scratch image
85+
// NOTE: QImage::fill doesn't like QPainter being active
86+
scratch_painter.setCompositionMode(QPainter::CompositionMode_Source);
87+
scratch_painter.fillRect(0, 0, SIZE, SIZE, Qt::transparent);
88+
scratch_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
89+
scratch_painter.drawText(0, SIZE, text);
90+
91+
int text_offset_x = 0;
92+
if (align_right) {
93+
// Figure out how far right we can shift it
94+
for (int col = SIZE-1; col >= 0; --col) {
95+
bool any_pixels = false;
96+
for (int row = SIZE-1; row >= 0; --row) {
97+
int opacity = qAlpha(scratch.pixel(col, row));
98+
if (opacity > 0) {
99+
any_pixels = true;
100+
break;
101+
}
102+
}
103+
if (any_pixels) {
104+
text_offset_x = (SIZE-1) - col;
105+
break;
106+
}
107+
}
108+
}
109+
110+
// Check if there's any overlap
111+
for (int row = 0; row < SIZE; ++row) {
112+
for (int col = text_offset_x; col < SIZE; ++col) {
113+
int opacity = qAlpha(icon.pixel(col, row));
114+
if (col >= text_offset_x) {
115+
opacity += qAlpha(scratch.pixel(col - text_offset_x, row));
116+
}
117+
if (opacity > 0xff) {
118+
// Overlap found, we're done
119+
return false;
120+
}
121+
}
122+
}
123+
return true;
124+
};
125+
int font_size = SIZE;
126+
while (font_size > 1) {
127+
font.setPixelSize(--font_size);
128+
scratch_painter.setFont(font);
129+
if (CheckSize(icon_in , label_in , /* align_right= */ false) &&
130+
CheckSize(icon_out, label_out, /* align_right= */ true)) break;
131+
}
132+
icon_in_painter .drawText(0, 0, SIZE, SIZE, Qt::AlignLeft | Qt::AlignBottom, label_in);
133+
icon_out_painter.drawText(0, 0, SIZE, SIZE, Qt::AlignRight | Qt::AlignBottom, label_out);
134+
}
135+
m_icon_conn_in = m_platform_style.TextColorIcon(QIcon(QPixmap::fromImage(icon_in)));
136+
m_icon_conn_out = m_platform_style.TextColorIcon(QIcon(QPixmap::fromImage(icon_out)));
137+
}
138+
38139
void PeerTableModel::startAutoRefresh()
39140
{
40141
timer->start();
@@ -76,11 +177,7 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
76177
case Address:
77178
return QString::fromStdString(rec->nodeStats.m_addr_name);
78179
case Direction:
79-
return QString(rec->nodeStats.fInbound ?
80-
//: An Inbound Connection from a Peer.
81-
tr("Inbound") :
82-
//: An Outbound Connection to a Peer.
83-
tr("Outbound"));
180+
return {};
84181
case ConnectionType:
85182
return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false);
86183
case Network:
@@ -97,11 +194,11 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
97194
assert(false);
98195
} else if (role == Qt::TextAlignmentRole) {
99196
switch (column) {
197+
case Direction:
100198
case NetNodeId:
101199
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
102200
case Address:
103201
return {};
104-
case Direction:
105202
case ConnectionType:
106203
case Network:
107204
return QVariant(Qt::AlignCenter);
@@ -115,6 +212,8 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
115212
assert(false);
116213
} else if (role == StatsRole) {
117214
return QVariant::fromValue(rec);
215+
} else if (index.column() == Direction && role == Qt::DecorationRole) {
216+
return rec->nodeStats.fInbound ? m_icon_conn_in : m_icon_conn_out;
118217
}
119218

120219
return QVariant();

src/qt/peertablemodel.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <net.h>
1010

1111
#include <QAbstractTableModel>
12+
#include <QIcon>
1213
#include <QList>
1314
#include <QModelIndex>
1415
#include <QStringList>
@@ -46,10 +47,11 @@ class PeerTableModel : public QAbstractTableModel
4647
void startAutoRefresh();
4748
void stopAutoRefresh();
4849

50+
// See also RPCConsole::ColumnWidths in rpcconsole.h
4951
enum ColumnIndex {
5052
NetNodeId = 0,
51-
Address,
5253
Direction,
54+
Address,
5355
ConnectionType,
5456
Network,
5557
Ping,
@@ -80,16 +82,16 @@ public Q_SLOTS:
8082
QList<CNodeCombinedStats> m_peers_data{};
8183
interfaces::Node& m_node;
8284
const PlatformStyle& m_platform_style;
85+
void DrawIcons();
86+
QIcon m_icon_conn_in, m_icon_conn_out;
8387
const QStringList columns{
8488
/*: Title of Peers Table column which contains a
8589
unique number used to identify a connection. */
8690
tr("Peer"),
91+
"", // Direction column has no title
8792
/*: Title of Peers Table column which contains the
8893
IP/Onion/I2P address of the connected peer. */
8994
tr("Address"),
90-
/*: Title of Peers Table column which indicates the direction
91-
the peer connection was initiated from. */
92-
tr("Direction"),
9395
/*: Title of Peers Table column which describes the type of
9496
peer connection. The "type" describes why the connection exists. */
9597
tr("Type"),

src/qt/rpcconsole.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ void RPCConsole::setClientModel(ClientModel *model, int bestblock_height, int64_
689689
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
690690

691691
if (!ui->peerWidget->horizontalHeader()->restoreState(m_peer_widget_header_state)) {
692+
ui->peerWidget->setColumnWidth(PeerTableModel::Direction, DIRECTION_COLUMN_WIDTH);
692693
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
693694
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
694695
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);

src/qt/rpcconsole.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ public Q_SLOTS:
144144

145145
enum ColumnWidths
146146
{
147+
DIRECTION_COLUMN_WIDTH = 32,
147148
ADDRESS_COLUMN_WIDTH = 200,
148149
SUBVERSION_COLUMN_WIDTH = 150,
149150
PING_COLUMN_WIDTH = 80,

0 commit comments

Comments
 (0)