Line data Source code
1 : // SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : /**
4 : * @file
5 : * @brief Self-refreshing widget showing an entry's current TOTP code with a
6 : * countdown bar and optional copy button.
7 : */
8 : #ifndef SRC_OTPCODEWIDGET_H_
9 : #define SRC_OTPCODEWIDGET_H_
10 :
11 : #include "totp.h"
12 :
13 : #include <QWidget>
14 :
15 : class QLabel;
16 : class QProgressBar;
17 : class QPushButtonWithClipboard;
18 :
19 : /**
20 : * @class OtpCodeWidget
21 : * @ingroup secrets
22 : * @brief Live one-time password display: code, copy button and countdown.
23 : *
24 : * Rendered by PasswordDisplayPanel as the value side of the "OTP code" row. It
25 : * is a widget rather than a set of loose children so that its refresh timer is
26 : * a child of the thing it updates: PasswordDisplayPanel::clear() deletes the
27 : * row's widgets, which destroys this widget and therefore the timer. A timer
28 : * owned by the panel instead could fire after its label had been deleted.
29 : *
30 : * The code is recomputed from the wall clock on every tick rather than from an
31 : * internal counter, so a machine that suspends across a period boundary
32 : * self-corrects on the first tick after resume.
33 : */
34 : class OtpCodeWidget : public QWidget {
35 15 : Q_OBJECT
36 :
37 : public:
38 : /**
39 : * @brief Build a live OTP code display and start its refresh timer.
40 : * @param settings Validated TOTP settings for the selected entry.
41 : * @param withCopyButton Add the clipboard button. Pass false when the
42 : * clipboard type is Enums::CLIPBOARD_NEVER.
43 : * @param monospace Render the code in a monospace font.
44 : * @param parent Parent widget.
45 : */
46 : OtpCodeWidget(Totp::Settings settings, bool withCopyButton, bool monospace,
47 : QWidget *parent = nullptr);
48 :
49 : /**
50 : * @brief Recompute the code and countdown for an explicit point in time.
51 : *
52 : * Called once per second by the internal timer; exposed so tests can pin the
53 : * clock instead of racing it.
54 : * @param unixTime Seconds since the Unix epoch (UTC).
55 : */
56 : void refresh(quint64 unixTime);
57 :
58 : /**
59 : * @brief The current code, ungrouped, exactly as it is copied.
60 : * @return The current OTP code.
61 : */
62 : [[nodiscard]] auto code() const -> QString;
63 :
64 : /**
65 : * @brief Seconds left on the current code, as last rendered.
66 : * @return A value in 1..Totp::Settings::step.
67 : */
68 : [[nodiscard]] auto secondsRemaining() const -> uint;
69 :
70 : signals:
71 : /**
72 : * @brief Emitted when the copy button is activated.
73 : * @param text The current code.
74 : */
75 : void copyRequested(const QString &text);
76 :
77 : private:
78 : /** @brief Validated TOTP parameters for the entry; fixed at construction. */
79 : Totp::Settings m_settings;
80 : /** @brief Copy button, or nullptr when built without one; child widget
81 : * whose text is kept equal to @ref m_code. */
82 : QPushButtonWithClipboard *m_copyButton{nullptr};
83 : /** @brief Shows the current code; child widget. */
84 : QLabel *m_codeLabel{nullptr};
85 : /** @brief Countdown bar from `step` down to 1 second; child widget. */
86 : QProgressBar *m_progress{nullptr};
87 : /** @brief The code last rendered, ungrouped. */
88 : QString m_code;
89 : /** @brief Time-step counter @ref m_code was generated for; a tick only
90 : * regenerates when the counter changes. */
91 : quint64 m_counter{0};
92 : /** @brief Seconds left on @ref m_code as of the last refresh(). */
93 : uint m_secondsRemaining{0};
94 : /** @brief False until the first refresh(), which must generate a code
95 : * regardless of @ref m_counter. */
96 : bool m_hasCode{false};
97 : };
98 :
99 : #endif // SRC_OTPCODEWIDGET_H_
|