Files
vdm/gui/src/dialogs/OptionsDialog.hpp
T
samiandClaude Sonnet 5 1fd2e0a0db gui: Options — Capture tab, saveTo.allowedRoots, lock coverage to the schema
Options was missing 8 of the 43 real settings.* keys: capture.enabled,
monitoredExtensions, monitoredMimeTypes, minSizeBytes, excludedHosts,
bypassModifier, autoStartTypes, and saveTo.allowedRoots. The capture.* keys
were dropped in an earlier pass on the mistaken read that the spec's "File
Types" tab meant per-category extension lists (which do live on Category,
not settings.*) — they're real settings.* keys for a real daemon feature
(the extension's auto-capture policy), so the tab exists now, named
"Capture" to match what it actually configures rather than the spec's
label.

New tst_optionsdialog case (allKeysMatchesTheSchemaExactly) loads
Settings.schema.json itself at test time and diffs its property set against
OptionsDialog::allKeys() — this drifted silently once already, so the
regression is now a build-time gate an unused import or a future key
addition would trip, not something that needs re-discovering by hand again.

Verified against a real veloxd (not just mockd): settings.get across all
43 keys, a settings.set/get round trip on a scalar (connection.timeoutSec)
and on array-valued keys in the shapes OptionsDialog::currentValues()
actually produces (capture.monitoredExtensions, proxy.bypassHosts,
saveTo.allowedRoots), and event.settings.changed fanning out to a second
subscribed client — all round-tripped and restored to their original
values afterward. The real OptionsDialog widget also loads and renders
correctly against that same daemon's live defaults with no crash under
ASan+UBSan.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
2026-09-12 21:29:19 +04:00

124 lines
3.6 KiB
C++

// The Options dialog. Lane GUI.
//
// docs/03-gui-spec.md §4: every control here maps 1:1 onto a settings.* key from
// contracts/schema/types/Settings.schema.json. The spec's "File Types" tab turned out to
// have real backing after all (capture.monitoredExtensions/monitoredMimeTypes/
// autoStartTypes are settings.* keys, not Category — a mistake in an earlier pass here,
// caught while checking this dialog covers all 43 keys against the now-live real veloxd);
// it is named "Capture" below to match what it actually configures. "Site Logins" still
// has no settings.* key (credentials go to the Secret Service) and stays out.
#pragma once
#include <QDialog>
#include <QJsonObject>
class QCheckBox;
class QComboBox;
class QLabel;
class QLineEdit;
class QPlainTextEdit;
class QSpinBox;
class QTabWidget;
namespace velox::gui {
namespace rpc {
class RpcClient;
} // namespace rpc
class OptionsDialog : public QDialog {
Q_OBJECT
public:
explicit OptionsDialog(rpc::RpcClient *client, QWidget *parent = nullptr);
/// The settings.* keys every tab's controls bind to, in the order settings.get is
/// called with. Exposed so a test can assert nothing here drifts from the schema.
static QStringList allKeys();
/// Pure: keys in `current` whose value differs from `original` (or is altogether
/// absent from `original`). settings.set is documented to write and broadcast exactly
/// the keys present in its params, so sending only what actually changed matters.
static QJsonObject diffChanged(const QJsonObject &original, const QJsonObject &current);
private slots:
void onSettingsLoaded(const QJsonObject &values);
void apply();
private:
void buildGeneralTab();
void buildCaptureTab();
void buildSaveToTab();
void buildConnectionTab();
void buildDownloadsTab();
void buildProxyTab();
void buildSoundsTab();
void populateFrom(const QJsonObject &values);
QJsonObject currentValues() const;
void browseInto(QLineEdit *target, bool pickFile);
rpc::RpcClient *client_;
QJsonObject original_; // last-known-saved values, keyed by settings.* name
QTabWidget *tabs_;
QLabel *statusLabel_;
// General
QCheckBox *launchOnLogin_;
QCheckBox *minimizeToTray_;
QCheckBox *showDropTarget_;
QCheckBox *confirmOnExit_;
QComboBox *language_;
QCheckBox *checkForUpdates_;
// Capture
QCheckBox *captureEnabled_;
QLineEdit *monitoredExtensions_;
QLineEdit *monitoredMimeTypes_;
QSpinBox *minSizeKiB_;
QLineEdit *excludedHosts_;
QComboBox *bypassModifier_;
QLineEdit *autoStartTypes_;
// Save To
QLineEdit *defaultDir_;
QLineEdit *tempDir_;
QComboBox *fileExistsPolicy_;
QCheckBox *createSubfolderPerSite_;
QPlainTextEdit *allowedRoots_;
// Connection
QComboBox *connectionPreset_;
QSpinBox *maxSegmentsPerDownload_;
QComboBox *bufferBytes_;
QSpinBox *maxConcurrentDownloads_;
QSpinBox *timeoutSec_;
QSpinBox *maxRetries_;
QSpinBox *retryBackoffSec_;
QSpinBox *maxTotalBufferMiB_;
QSpinBox *maxActiveSegments_;
// Downloads
QCheckBox *speedLimitEnabled_;
QSpinBox *speedLimitKiBps_;
QLineEdit *virusScanCommand_;
QLineEdit *postDownloadCommand_;
QComboBox *duplicatePolicy_;
QCheckBox *verifyChecksums_;
// Proxy
QComboBox *proxyMode_;
QLineEdit *proxyHost_;
QSpinBox *proxyPort_;
QLineEdit *proxyUsername_;
QLineEdit *proxyBypassHosts_;
QLineEdit *proxyPacUrl_;
// Sounds
QCheckBox *soundsEnabled_;
QLineEdit *soundOnComplete_;
QLineEdit *soundOnQueueComplete_;
QLineEdit *soundOnError_;
};
} // namespace velox::gui