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

IJHack / QtPass / 28659639755

03 Jul 2026 12:10PM UTC coverage: 56.754% (+0.03%) from 56.72%
28659639755

push

github

web-flow
fix: keep clipboard autoclear tracking when navigating between entries (#1607)

* fix: keep clipboard autoclear tracking when navigating between entries

clippedText records what copyTextToClipboard() placed on the clipboard so the
autoclear timer knows what to clear. But setClippedText() (called on every entry
show) and clearClippedText() (called on every tree navigation) both overwrote
it, so after copying a password and then selecting another entry the timer saw
clippedText != clipboard and refused to clear — leaving the copied password on
the clipboard indefinitely, past its autoclear window.

The staged value was never consumed anywhere (on-demand copy uses the field
value directly), so it only corrupted the autoclear tracker. Make clippedText a
pure record of the current clipboard contents: setClippedText() now only copies
in "always copy" mode (via copyTextToClipboard, which sets the tracker) and no
longer overwrites it otherwise, and the vestigial clearClippedText() and its
navigation call are removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuQsrHonihp1nARE7bzstc

* docs: update setClippedText docstring to match conditional copy behavior

setClippedText only copies (and thus updates the autoclear tracker) in
"always copy" mode; document that instead of implying it always mutates the
tracked value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuQsrHonihp1nARE7bzstc

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

0 of 2 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

3811 of 6715 relevant lines covered (56.75%)

30.8 hits per line

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

37.11
/src/qtpass.cpp
1
// SPDX-FileCopyrightText: 2018 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "qtpass.h"
4
#include "mainwindow.h"
5
#include "qtpasssettings.h"
6
#include "util.h"
7
#include <QApplication>
8
#include <QClipboard>
9
#include <QDialog>
10
#include <QLabel>
11
#include <QPixmap>
12
#include <QVBoxLayout>
13

14
#ifndef Q_OS_WIN
15
#include <QInputDialog>
16
#include <QLineEdit>
17
#include <QMimeData>
18
#include <utility>
19
#else
20
#define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
21
#define WIN32_EXTRA_LEAN
22
#include <windows.h>
23
#include <winnetwk.h>
24
#undef DELETE
25
#include <QMimeData>
26
#endif
27

28
#ifdef QT_DEBUG
29
#include "debughelper.h"
30
#endif
31

32
/**
33
 * @brief Constructs a QtPass instance.
34
 * @param mainWindow The main window reference
35
 */
36
QtPass::QtPass(MainWindow *mainWindow) : m_mainWindow(mainWindow) {
12✔
37
  setClipboardTimer();
12✔
38
  clearClipboardTimer.setSingleShot(true);
12✔
39
  connect(&clearClipboardTimer, &QTimer::timeout, this,
12✔
40
          &QtPass::clearClipboard);
12✔
41

42
  QObject::connect(QApplication::instance(), &QApplication::aboutToQuit, this,
12✔
43
                   &QtPass::clearClipboard);
12✔
44

45
  setMainWindow();
12✔
46
}
12✔
47

48
/**
49
 * @brief QtPass::~QtPass destroy!
50
 */
51
QtPass::~QtPass() {
24✔
52
#ifdef Q_OS_WIN
53
  const AppSettings s = QtPassSettings::load();
54
  if (s.useWebDav)
55
    WNetCancelConnection2A(s.passStore.toUtf8().constData(), 0, 1);
56
#else
57
  if (fusedav.state() == QProcess::Running) {
12✔
58
    fusedav.terminate();
×
59
    fusedav.waitForFinished(2000);
×
60
  }
61
#endif
62
}
24✔
63

64
/**
65
 * @brief QtPass::init make sure we are ready to go as soon as
66
 * possible
67
 */
68
auto QtPass::init() -> bool {
12✔
69
  QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
12✔
70
  QtPassSettings::setPassStore(passStore);
12✔
71

72
  QtPassSettings::initExecutables();
12✔
73

74
  QString version = QtPassSettings::getVersion();
24✔
75

76
  // Config updates
77
  if (version.isEmpty()) {
12✔
78
#ifdef QT_DEBUG
79
    dbg() << "assuming fresh install";
80
#endif
81
    AppSettings s = QtPassSettings::load();
×
82
    if (s.autoclearSeconds < 5)
×
83
      s.autoclearSeconds = 10;
×
84
    if (s.autoclearPanelSeconds < 5)
×
85
      s.autoclearPanelSeconds = 10;
×
86
    s.usePwgen = !s.pwgenExecutable.isEmpty();
×
87
    s.passTemplate = QStringLiteral("login\nurl");
×
88
    QtPassSettings::save(s);
×
89
  } else {
×
90
    AppSettings s = QtPassSettings::load();
12✔
91
    if (s.passTemplate.isEmpty()) {
12✔
92
      s.passTemplate = QStringLiteral("login\nurl");
×
93
      QtPassSettings::save(s);
×
94
    }
95
  }
12✔
96

97
  QtPassSettings::setVersion(VERSION);
12✔
98

99
  if (!Util::configIsValid(QtPassSettings::load())) {
12✔
100
    m_mainWindow->config();
×
101
    if (freshStart && !Util::configIsValid(QtPassSettings::load())) {
×
102
      return false;
103
    }
104
  }
105

106
  // Note: WebDAV mount needs to happen before accessing the store,
107
  // but ideally should be done after Window is shown to avoid long delay.
108
  if (QtPassSettings::isUseWebDav()) {
12✔
109
    mountWebDav();
×
110
  }
111

112
  freshStart = false;
12✔
113
  return true;
12✔
114
}
115

116
/**
117
 * @brief Sets up the main window and connects signal handlers.
118
 */
119
void QtPass::setMainWindow() {
12✔
120
  m_mainWindow->restoreWindow();
12✔
121

122
  fusedav.setParent(m_mainWindow);
12✔
123

124
  // Signal handlers are connected for both pass implementations
125
  // Note: When pass binary changes, QtPass restart is required to reconnect
126
  // This is acceptable as pass binary change is infrequent
127
  connectPassSignalHandlers(QtPassSettings::getRealPass());
12✔
128
  connectPassSignalHandlers(QtPassSettings::getImitatePass());
12✔
129

130
  connect(m_mainWindow, &MainWindow::passShowHandlerFinished, this,
12✔
131
          &QtPass::passShowHandlerFinished);
12✔
132

133
  // only for ipass
134
  connect(QtPassSettings::getImitatePass(), &ImitatePass::startReencryptPath,
12✔
135
          m_mainWindow, &MainWindow::startReencryptPath);
12✔
136
  connect(QtPassSettings::getImitatePass(), &ImitatePass::endReencryptPath,
12✔
137
          m_mainWindow, &MainWindow::endReencryptPath);
12✔
138

139
  connect(m_mainWindow, &MainWindow::passGitInitNeeded, []() {
12✔
140
#ifdef QT_DEBUG
141
    dbg() << "Pass git init called";
142
#endif
143
    QtPassSettings::getPass()->GitInit();
×
144
  });
×
145

146
  connect(m_mainWindow, &MainWindow::generateGPGKeyPair, m_mainWindow,
12✔
147
          [this](const QString &batch) {
12✔
148
            QtPassSettings::getPass()->GenerateGPGKeys(batch);
×
149
            m_mainWindow->showStatusMessage(tr("Generating GPG key pair"),
×
150
                                            60000);
151
          });
×
152
}
12✔
153

154
/**
155
 * @brief Connects pass signal handlers to QtPass slots.
156
 * @param pass The pass instance to connect
157
 */
158
void QtPass::connectPassSignalHandlers(Pass *pass) {
24✔
159
  connect(pass, &Pass::error, this, &QtPass::processError);
24✔
160
  connect(pass, &Pass::processErrorExit, this, &QtPass::processErrorExit);
24✔
161
  connect(pass, &Pass::critical, m_mainWindow, &MainWindow::critical);
24✔
162
  connect(pass, &Pass::startingExecuteWrapper, m_mainWindow,
24✔
163
          &MainWindow::executeWrapperStarted);
24✔
164
  connect(pass, &Pass::statusMsg, m_mainWindow, &MainWindow::showStatusMessage);
24✔
165
  connect(pass, &Pass::finishedShow, m_mainWindow,
24✔
166
          &MainWindow::passShowHandler);
24✔
167
  connect(pass, &Pass::finishedOtpGenerate, m_mainWindow,
24✔
168
          &MainWindow::passOtpHandler);
24✔
169

170
  connect(pass, &Pass::finishedGitInit, this, &QtPass::passStoreChanged);
24✔
171
  connect(pass, &Pass::finishedGitPull, this, &QtPass::processFinished);
24✔
172
  connect(pass, &Pass::finishedGitPush, this, &QtPass::processFinished);
24✔
173
  connect(pass, &Pass::finishedInsert, this, &QtPass::finishedInsert);
24✔
174
  connect(pass, &Pass::finishedRemove, this, &QtPass::passStoreChanged);
24✔
175
  connect(pass, &Pass::finishedInit, this, &QtPass::passStoreChanged);
24✔
176
  connect(pass, &Pass::finishedMove, this, &QtPass::passStoreChanged);
24✔
177
  connect(pass, &Pass::finishedCopy, this, &QtPass::passStoreChanged);
24✔
178
  connect(pass, &Pass::finishedGenerateGPGKeys, this,
24✔
179
          &QtPass::onKeyGenerationComplete);
24✔
180
  connect(pass, &Pass::finishedGrep, m_mainWindow, &MainWindow::onGrepFinished);
24✔
181
}
24✔
182

183
/**
184
 * @brief QtPass::mountWebDav is some scary voodoo magic
185
 */
186
void QtPass::mountWebDav() {
×
187
  const AppSettings s = QtPassSettings::load();
×
188
#ifdef Q_OS_WIN
189
  char dst[20] = {0};
190
  NETRESOURCEA netres;
191
  memset(&netres, 0, sizeof(netres));
192
  netres.dwType = RESOURCETYPE_DISK;
193
  netres.lpLocalName = nullptr;
194
  // Store QByteArray in variables to ensure lifetime during WNetUseConnectionA
195
  // call
196
  QByteArray webDavUrlUtf8 = s.webDavUrl.toUtf8();
197
  QByteArray webDavPasswordUtf8 = s.webDavPassword.toUtf8();
198
  QByteArray webDavUserUtf8 = s.webDavUser.toUtf8();
199
  netres.lpRemoteName = const_cast<char *>(webDavUrlUtf8.constData());
200
  DWORD size = sizeof(dst);
201
  DWORD r = WNetUseConnectionA(
202
      reinterpret_cast<HWND>(m_mainWindow->effectiveWinId()), &netres,
203
      const_cast<char *>(webDavPasswordUtf8.constData()),
204
      const_cast<char *>(webDavUserUtf8.constData()),
205
      CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
206
      0);
207
  if (r == NO_ERROR) {
208
    QtPassSettings::setPassStore(dst);
209
  } else {
210
    char message[256] = {0};
211
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
212
                   sizeof(message), 0);
213
    m_mainWindow->flashText(tr("Failed to connect WebDAV:\n") + message +
214
                                " (0x" + QString::number(r, 16) + ")",
215
                            true);
216
  }
217
#else
218
  fusedav.start("fusedav", QStringList()
×
219
                               << "-o"
×
220
                               << "nonempty"
×
221
                               << "-u"
×
222
                               << "\"" + s.webDavUser + "\"" << s.webDavUrl
×
223
                               << "\"" + s.passStore + "\"");
×
224
  fusedav.waitForStarted();
×
225
  if (fusedav.state() == QProcess::Running) {
×
226
    QString pwd = s.webDavPassword;
227
    bool ok = true;
×
228
    if (pwd.isEmpty()) {
×
229
      pwd = QInputDialog::getText(m_mainWindow, tr("QtPass WebDAV password"),
×
230
                                  tr("Enter password to connect to WebDAV:"),
×
231
                                  QLineEdit::Password, "", &ok);
232
    }
233
    if (ok && !pwd.isEmpty()) {
×
234
      fusedav.write(pwd.toUtf8() + '\n');
×
235
      fusedav.closeWriteChannel();
×
236
      fusedav.waitForFinished(2000);
×
237
    } else {
238
      fusedav.terminate();
×
239
    }
240
  }
241
  QString error = fusedav.readAllStandardError();
×
242
  qsizetype prompt = error.indexOf("Password:");
×
243
  if (prompt >= 0) {
×
244
    error.remove(0, prompt + 10);
×
245
  }
246
  if (fusedav.state() != QProcess::Running) {
×
247
    error = tr("fusedav exited unexpectedly\n") + error;
×
248
  }
249
  if (error.size() > 0) {
×
250
    m_mainWindow->flashText(
×
251
        tr("Failed to start fusedav to connect WebDAV:\n") + error, true);
×
252
  }
253
#endif
254
}
×
255

256
/**
257
 * @brief QtPass::processError something went wrong
258
 * @param error
259
 */
260
void QtPass::processError(QProcess::ProcessError error) {
×
261
  QString errorString;
×
262
  switch (error) {
×
263
  case QProcess::FailedToStart:
×
264
    errorString = tr("QProcess::FailedToStart");
×
265
    break;
×
266
  case QProcess::Crashed:
×
267
    errorString = tr("QProcess::Crashed");
×
268
    break;
×
269
  case QProcess::Timedout:
×
270
    errorString = tr("QProcess::Timedout");
×
271
    break;
×
272
  case QProcess::ReadError:
×
273
    errorString = tr("QProcess::ReadError");
×
274
    break;
×
275
  case QProcess::WriteError:
×
276
    errorString = tr("QProcess::WriteError");
×
277
    break;
×
278
  case QProcess::UnknownError:
×
279
    errorString = tr("QProcess::UnknownError");
×
280
    break;
×
281
  }
282
  m_mainWindow->flashText(errorString, true);
×
283
  m_mainWindow->setUiElementsEnabled(true);
×
284
}
×
285

286
/**
287
 * @brief Handles process error exit.
288
 * @param exitCode The exit code
289
 * @param p_error The error message
290
 */
291
void QtPass::processErrorExit(int exitCode, const QString &p_error) {
×
292
  if (nullptr != m_mainWindow->getKeyGenDialog()) {
×
293
    m_mainWindow->cleanKeygenDialog();
×
294
    if (exitCode != 0) {
×
295
      m_mainWindow->showStatusMessage(tr("GPG key pair generation failed"),
×
296
                                      10000);
297
    }
298
  }
299

300
  if (!p_error.isEmpty()) {
×
301
    QString output;
×
302
    QString error = p_error.toHtmlEscaped();
×
303
    if (exitCode == 0) {
×
304
      //  https://github.com/IJHack/qtpass/issues/111
305
      output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
×
306
    } else {
307
      output = "<span style=\"color: red;\">" + error + "</span><br />";
×
308
    }
309

310
    output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
×
311
    output.replace(QStringLiteral("\n"), "<br />");
×
312

313
    m_mainWindow->flashText(output, false, true);
×
314
  }
315

316
  m_mainWindow->setUiElementsEnabled(true);
×
317
}
×
318

319
/**
320
 * @brief QtPass::processFinished background process has finished
321
 * @param exitCode
322
 * @param exitStatus
323
 * @param output    stdout from a process
324
 * @param errout    stderr from a process
325
 */
326
void QtPass::processFinished(const QString &p_output, const QString &p_errout) {
×
327
  showInTextBrowser(p_output);
×
328
  //    Sometimes there is error output even with 0 exit code, which is
329
  //    assumed in this function
330
  processErrorExit(0, p_errout);
×
331

332
  m_mainWindow->setUiElementsEnabled(true);
×
333
}
×
334

335
/**
336
 * @brief Called when pass store has changed.
337
 * @param p_out Output from the process
338
 * @param p_err Error output
339
 */
340
void QtPass::passStoreChanged(const QString &p_out, const QString &p_err) {
×
341
  processFinished(p_out, p_err);
×
342
  doGitPush();
×
343
}
×
344

345
/**
346
 * @brief Called when an insert operation has finished.
347
 * @param p_output Output from the process
348
 * @param p_errout Error output
349
 */
350
void QtPass::finishedInsert(const QString &p_output, const QString &p_errout) {
×
351
  processFinished(p_output, p_errout);
×
352
  doGitPush();
×
353
  m_mainWindow->on_treeView_clicked(m_mainWindow->getCurrentTreeViewIndex());
×
354
}
×
355

356
/**
357
 * @brief Called when GPG key generation is complete.
358
 * @param p_output Standard output from the key generation process
359
 * @param p_errout Standard error output from the key generation process
360
 */
361
void QtPass::onKeyGenerationComplete(const QString &p_output,
×
362
                                     const QString &p_errout) {
363
  if (nullptr != m_mainWindow->getKeyGenDialog()) {
×
364
#ifdef QT_DEBUG
365
    qDebug() << "Keygen Done";
366
#endif
367

368
    m_mainWindow->cleanKeygenDialog();
×
369
    m_mainWindow->showStatusMessage(tr("GPG key pair generated successfully"),
×
370
                                    10000);
371
  }
372

373
  processFinished(p_output, p_errout);
×
374
}
×
375

376
/**
377
 * @brief Called when the password show handler has finished.
378
 * @param output The password content to display
379
 */
380
void QtPass::passShowHandlerFinished(QString output) {
×
381
  showInTextBrowser(std::move(output));
×
382
}
×
383

384
/**
385
 * @brief Displays output text in the main window's text browser.
386
 * @param output The text to display
387
 * @param prefix Optional prefix to prepend to the output
388
 * @param postfix Optional postfix to append to the output
389
 */
390
void QtPass::showInTextBrowser(QString output, const QString &prefix,
×
391
                               const QString &postfix) {
392
  output = output.toHtmlEscaped();
×
393

394
  output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
×
395
  output.replace(QStringLiteral("\n"), "<br />");
×
396
  output = prefix + output + postfix;
×
397

398
  m_mainWindow->flashText(output, false, true);
×
399
}
×
400

401
/**
402
 * @brief Performs automatic git push if enabled in settings.
403
 */
404
void QtPass::doGitPush() {
×
405
  if (QtPassSettings::isAutoPush()) {
×
406
    m_mainWindow->onPush();
×
407
  }
408
}
×
409

410
/**
411
 * @brief Sets the text to be stored in clipboard and handles clipboard
412
 * operations.
413
 * @param password The password or text to store
414
 * @param p_output Additional output text
415
 */
416
void QtPass::setClippedText(const QString &password, const QString &p_output) {
×
417
  const AppSettings s = QtPassSettings::load();
×
418
  // Only "always copy" mode actually places text on the clipboard here; in
419
  // on-demand mode the copy happens later from the field's copy button.
420
  // Crucially, do not touch clippedText otherwise — it tracks what is currently
421
  // on the clipboard so the autoclear timer knows what to clear. Overwriting it
422
  // when merely showing another entry left a previously copied password on the
423
  // clipboard past its autoclear window (copyTextToClipboard sets it).
NEW
424
  if (s.clipBoardType == Enums::CLIPBOARD_ALWAYS && !p_output.isEmpty()) {
×
NEW
425
    copyTextToClipboard(password);
×
426
  }
427
}
×
428

429
/**
430
 * @brief Sets the clipboard clear timer based on autoclear settings.
431
 */
432
void QtPass::setClipboardTimer() {
12✔
433
  clearClipboardTimer.setInterval(MS_PER_SECOND *
24✔
434
                                  QtPassSettings::getAutoclearSeconds());
24✔
435
}
12✔
436

437
/**
438
 * @brief MainWindow::clearClipboard remove clipboard contents.
439
 */
440
void QtPass::clearClipboard() {
1✔
441
  QClipboard *clipboard = QApplication::clipboard();
1✔
442
  bool cleared = false;
443
  if (this->clippedText == clipboard->text(QClipboard::Selection)) {
1✔
444
    clipboard->clear(QClipboard::Selection);
1✔
445
    clipboard->setText(QString(""), QClipboard::Selection);
2✔
446
    cleared = true;
447
  }
448
  if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
2✔
449
    clipboard->clear(QClipboard::Clipboard);
1✔
450
    cleared = true;
451
  }
452
  if (cleared) {
×
453
    m_mainWindow->showStatusMessage(tr("Clipboard cleared"));
2✔
454
  } else {
455
    m_mainWindow->showStatusMessage(tr("Clipboard not cleared"));
×
456
  }
457

458
  clippedText.clear();
1✔
459
}
1✔
460

461
/**
462
 * @brief Build clipboard MIME data with platform-specific security hints.
463
 * @param text - Plain text to copy
464
 * @return QMimeData with text and security hints
465
 */
466
auto buildClipboardMimeData(const QString &text) -> QMimeData * {
1✔
467
  auto *mimeData = new QMimeData();
1✔
468
  mimeData->setText(text);
1✔
469
#ifdef Q_OS_LINUX
470
  mimeData->setData("x-kde-passwordManagerHint", QByteArray("secret"));
2✔
471
#endif
472
#ifdef Q_OS_MAC
473
  mimeData->setData("application/x-nspasteboard-concealed-type", QByteArray());
474
#endif
475
#ifdef Q_OS_WIN
476
  mimeData->setData("ExcludeClipboardContentFromMonitorProcessing",
477
                    dwordBytes(1));
478
  mimeData->setData("CanIncludeInClipboardHistory", dwordBytes(0));
479
  mimeData->setData("CanUploadToCloudClipboard", dwordBytes(0));
480
#endif
481
  return mimeData;
1✔
482
}
483

484
/**
485
 * @brief MainWindow::copyTextToClipboard copies text to your clipboard
486
 * @param text
487
 */
488
void QtPass::copyTextToClipboard(const QString &text) {
×
489
  const AppSettings s = QtPassSettings::load();
×
490
  QClipboard *clip = QApplication::clipboard();
×
491

492
  QClipboard::Mode mode = QClipboard::Clipboard;
493
  if (s.useSelection && clip->supportsSelection()) {
×
494
    mode = QClipboard::Selection;
495
  }
496

497
  auto *mimeData = buildClipboardMimeData(text);
×
498
  clip->setMimeData(mimeData, mode);
×
499

500
  clippedText = text;
×
501
  m_mainWindow->showStatusMessage(tr("Copied to clipboard"));
×
502
  if (s.useAutoclear) {
×
503
    clearClipboardTimer.start();
×
504
  }
505
}
×
506

507
/**
508
 * @brief displays the text as qrcode
509
 * @param text
510
 */
511
void QtPass::showTextAsQRCode(const QString &text) {
×
512
  const AppSettings s = QtPassSettings::load();
×
513
  const QString qrExe = s.qrencodeExecutable.isEmpty()
514
                            ? QStringLiteral("/usr/bin/qrencode")
×
515
                            : s.qrencodeExecutable;
516
  QProcess qrencode;
×
517
  qrencode.start(qrExe, QStringList() << "-o-"
×
518
                                      << "-tPNG");
×
519
  qrencode.write(text.toUtf8());
×
520
  qrencode.closeWriteChannel();
×
521
  qrencode.waitForFinished();
×
522
  QByteArray output(qrencode.readAllStandardOutput());
×
523

524
  if (qrencode.exitStatus() || qrencode.exitCode()) {
×
525
    QString error(qrencode.readAllStandardError());
×
526
    m_mainWindow->showStatusMessage(error);
×
527
  } else {
528
    QPixmap image;
×
529
    image.loadFromData(output, "PNG");
×
530
    QDialog *popup = createQRCodePopup(image);
×
531
    popup->exec();
×
532
  }
×
533
}
×
534

535
/**
536
 * @brief QtPass::createQRCodePopup creates a popup dialog with the given QR
537
 * code image. This is extracted for testability. The caller is responsible
538
 * for showing and managing the popup lifecycle.
539
 * @param image The QR code pixmap to display
540
 * @return The created popup dialog
541
 */
542
QDialog *QtPass::createQRCodePopup(const QPixmap &image) {
1✔
543
  auto *popup = new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint);
1✔
544
  popup->setAttribute(Qt::WA_DeleteOnClose);
1✔
545
  auto *layout = new QVBoxLayout;
1✔
546
  auto *popupLabel = new QLabel();
1✔
547
  layout->addWidget(popupLabel);
1✔
548
  popupLabel->setPixmap(image);
1✔
549
  popupLabel->setScaledContents(true);
1✔
550
  popupLabel->show();
1✔
551
  popup->setLayout(layout);
1✔
552
  popup->move(QCursor::pos());
1✔
553
  return popup;
1✔
554
}
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