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
124 lines
3.6 KiB
C++
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 ¤t);
|
|
|
|
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
|