• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 27500738735

14 Jun 2026 01:43PM UTC coverage: 57.418%. First build
27500738735

Pull #1535

github

web-flow
Merge fd7a5bb80 into 09848c3f0
Pull Request #1535: refactor: extract field rendering into PasswordDisplayPanel (#1512)

76 of 100 new or added lines in 2 files covered. (76.0%)

3971 of 6916 relevant lines covered (57.42%)

36.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.53
/src/passworddisplaypanel.cpp
1
// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3

4
/**
5
 * @class PasswordDisplayPanel
6
 * @brief Password-field rendering implementation.
7
 *
8
 * @see passworddisplaypanel.h
9
 */
10

11
#include "passworddisplaypanel.h"
12
#include "qpushbuttonasqrcode.h"
13
#include "qpushbuttonshowpassword.h"
14
#include "qpushbuttonwithclipboard.h"
15
#include "qtpasssettings.h"
16
#include "util.h"
17

18
#include <QBoxLayout>
19
#include <QDesktopServices>
20
#include <QFrame>
21
#include <QGridLayout>
22
#include <QHBoxLayout>
23
#include <QIcon>
24
#include <QLabel>
25
#include <QLineEdit>
26
#include <QPalette>
27
#include <QPushButton>
28
#include <QTextBrowser>
29
#include <QUrl>
30

31
PasswordDisplayPanel::PasswordDisplayPanel(QGridLayout *grid,
16✔
32
                                           QBoxLayout *container,
33
                                           QWidget *widgetParent,
34
                                           QObject *parent)
16✔
35
    : QObject(parent), m_grid(grid), m_container(container),
16✔
36
      m_widgetParent(widgetParent) {}
16✔
37

38
void PasswordDisplayPanel::clear() {
2✔
39
  while (m_grid->count() > 0) {
4✔
40
    QLayoutItem *item = m_grid->takeAt(0);
2✔
41
    delete item->widget();
2✔
42
    delete item;
2✔
43
  }
44
  m_container->setSpacing(0);
2✔
45
}
2✔
46

47
void PasswordDisplayPanel::displayFields(const QString &password,
3✔
48
                                         const NamedValues &namedValues) {
49
  if (!password.isEmpty()) {
3✔
50
    // The password is hidden in addField when needed.
51
    addField(0, QObject::tr("Password"), password);
4✔
52
  }
53
  for (int j = 0; j < namedValues.length(); ++j) {
4✔
54
    const NamedValue &nv = namedValues.at(j);
55
    addField(j + 1, nv.name, nv.value);
1✔
56
  }
57
  m_container->setSpacing(m_grid->count() == 0 ? 0 : 6);
5✔
58
}
3✔
59

60
void PasswordDisplayPanel::appendField(const QString &field,
1✔
61
                                       const QString &value) {
62
  // Each row is two grid items (label + value frame), so the next free row is
63
  // count() / 2 — the same sequential scheme displayFields() uses.
64
  addField(m_grid->count() / 2, field, value);
1✔
65
}
1✔
66

67
void PasswordDisplayPanel::addField(int position, const QString &field,
4✔
68
                                    const QString &value) {
69
  QString trimmedField = field.trimmed();
70
  QString trimmedValue = value.trimmed();
71

72
  const QString buttonStyle =
73
      "border-style: none; background: transparent; padding: 0; margin: 0; "
74
      "icon-size: 16px; color: inherit;";
4✔
75

76
  // Combine the Copy button and the line edit in one widget
77
  auto *frame = new QFrame();
4✔
78
  QLayout *ly = new QHBoxLayout();
4✔
79
  ly->setContentsMargins(5, 2, 2, 2);
4✔
80
  ly->setSpacing(0);
4✔
81
  frame->setLayout(ly);
4✔
82
  if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER) {
4✔
83
    auto *fieldLabel =
84
        new QPushButtonWithClipboard(trimmedValue, m_widgetParent);
4✔
85
    connect(fieldLabel, &QPushButtonWithClipboard::clicked, this,
4✔
86
            &PasswordDisplayPanel::copyRequested);
4✔
87

88
    fieldLabel->setStyleSheet(buttonStyle);
4✔
89
    frame->layout()->addWidget(fieldLabel);
4✔
90
  }
91

92
  if (QtPassSettings::isUseQrencode()) {
4✔
NEW
93
    auto *qrbutton = new QPushButtonAsQRCode(trimmedValue, m_widgetParent);
×
NEW
94
    connect(qrbutton, &QPushButtonAsQRCode::clicked, this,
×
NEW
95
            &PasswordDisplayPanel::qrRequested);
×
NEW
96
    qrbutton->setStyleSheet(buttonStyle);
×
NEW
97
    frame->layout()->addWidget(qrbutton);
×
98
  }
99

100
  // Show an explicit "open in browser" button when the value is a safe
101
  // http(s) URL. The inline clickable link still works for URLs embedded in
102
  // prose; this button is the discoverable affordance for url fields.
103
  // Never on the password field: its value is a secret and must not be
104
  // surfaced in a tooltip or handed to the browser.
105
  if (trimmedField != QObject::tr("Password") &&
10✔
106
      Util::isLaunchableWebUrl(trimmedValue)) {
2✔
107
    auto *urlButton = new QPushButton(m_widgetParent);
1✔
108
    urlButton->setIcon(QIcon::fromTheme(QStringLiteral("applications-internet"),
2✔
109
                                        QIcon(":/icons/open-url.svg")));
2✔
110
    urlButton->setToolTip(
2✔
111
        QObject::tr("Open %1 in browser").arg(trimmedValue.toHtmlEscaped()));
2✔
112
    urlButton->setStyleSheet(buttonStyle);
1✔
113
    urlButton->setCursor(Qt::PointingHandCursor);
1✔
114
    connect(urlButton, &QPushButton::clicked, this, [trimmedValue]() {
1✔
115
      // Re-validate before launching (defence in depth: the value is
116
      // immutable here, but never hand an unvalidated string to the OS
117
      // URL handler).
NEW
118
      if (Util::isLaunchableWebUrl(trimmedValue)) {
×
NEW
119
        QDesktopServices::openUrl(QUrl(trimmedValue));
×
120
      }
NEW
121
    });
×
122
    frame->layout()->addWidget(urlButton);
1✔
123
  }
124

125
  // set the echo mode to password, if the field is "password"
126
  const QString lineStyle =
127
      QtPassSettings::isUseMonospace()
8✔
128
          ? "border-style: none; background: transparent; font-family: "
129
            "monospace;"
130
          : "border-style: none; background: transparent;";
8✔
131

132
  if (QtPassSettings::isHidePassword() &&
8✔
133
      trimmedField == QObject::tr("Password")) {
4✔
NEW
134
    auto *line = new QLineEdit();
×
NEW
135
    line->setObjectName(trimmedField);
×
NEW
136
    line->setText(trimmedValue);
×
NEW
137
    line->setReadOnly(true);
×
NEW
138
    line->setStyleSheet(lineStyle);
×
NEW
139
    line->setContentsMargins(0, 0, 0, 0);
×
NEW
140
    line->setEchoMode(QLineEdit::Password);
×
NEW
141
    auto *showButton = new QPushButtonShowPassword(line, m_widgetParent);
×
NEW
142
    showButton->setStyleSheet(buttonStyle);
×
NEW
143
    showButton->setContentsMargins(0, 0, 0, 0);
×
NEW
144
    frame->layout()->addWidget(showButton);
×
NEW
145
    frame->layout()->addWidget(line);
×
146
  } else {
147
    auto *line = new QTextBrowser();
4✔
148
    line->setOpenExternalLinks(true);
4✔
149
    line->setOpenLinks(true);
4✔
150
    line->setMaximumHeight(26);
4✔
151
    line->setMinimumHeight(26);
4✔
152
    line->setSizePolicy(
4✔
153
        QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
154
    line->setObjectName(trimmedField);
4✔
155
    trimmedValue.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
4✔
156
    line->setText(trimmedValue);
4✔
157
    line->setReadOnly(true);
4✔
158
    line->setStyleSheet(lineStyle);
4✔
159
    line->setContentsMargins(0, 0, 0, 0);
4✔
160
    frame->layout()->addWidget(line);
4✔
161
  }
162

163
  // Derive the border colour from the palette so it adapts to light/dark
164
  // themes instead of a hardcoded light grey.
165
  const QString borderColor =
166
      m_widgetParent->palette().color(QPalette::Mid).name();
8✔
167
  frame->setStyleSheet(QStringLiteral(".QFrame{border: 1px solid %1; "
8✔
168
                                      "border-radius: 5px;}")
169
                           .arg(borderColor));
8✔
170

171
  // set into the layout
172
  m_grid->addWidget(new QLabel(trimmedField), position, 0);
4✔
173
  m_grid->addWidget(frame, position, 1);
4✔
174
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc