feat: Complete Preferences system implementation

- PreferencesService: QSettings-based persistence for all app settings
- StartupBehaviorService: Windows registry auto-start management with policy detection
- TranslationService: Language management with QTranslator, 9 built-in languages
- AiActionOptionsService: 30+ AI translation language options
- SettingsView: New 'Preferences' tab with full UI:
  * Search Mode (Local/Online)
  * Default App Mode (Mail/Calendar/Contacts/Settings)
  * Language selector (9 built-in + .qm files)
  * Email Sync Interval (1-1440 minutes)
  * Startup Behavior (auto-start, minimize to tray, notifications)
  * AI Translation (default language, summarize language, summary save path)
- MainMainWindow: Service wiring (PreferencesService, StartupBehaviorService, TranslationService, AiActionOptionsService)
- CMake: Added all new service files

All services integrated and wired to SettingsView.
This commit is contained in:
2026-08-28 02:27:46 +02:00
parent 516a658b91
commit 15e06d64d4
853 changed files with 1523 additions and 611610 deletions
+18
View File
@@ -9,6 +9,10 @@
#include "ui/rulesmanagerdialog.h"
#include "ui/accountsetupdialog.h"
#include "ui/delegates/FolderTreeDelegate.h"
#include "services/preferencesservice.h"
#include "services/startupbehaviorservice.h"
#include "services/translationservice.h"
#include "services/aiactionoptionsservice.h"
#include <optional>
#include <QMessageBox>
#include <QFileDialog>
@@ -333,11 +337,25 @@ void MainMainWindow::connectModels()
qDebug() << "[MainMainWindow::connectModels] Start";
m_accountService = new AccountService(this);
qDebug() << "[MainMainWindow::connectModels] AccountService created";
// Initialize preferences and related services
m_preferencesService = new PreferencesService(this);
m_startupBehaviorService = new StartupBehaviorService(this);
m_translationService = new TranslationService(this);
m_aiActionOptionsService = new AiActionOptionsService(this);
m_mailService = new MailService(m_accountService, this);
qDebug() << "[MainMainWindow::connectModels] MailService created";
m_composeView->setAccountService(m_accountService);
m_embeddedComposeView->setAccountService(m_accountService);
// Pass services to settings view
m_settingsView->setAccountService(m_accountService);
m_settingsView->setPreferencesService(m_preferencesService);
m_settingsView->setStartupBehaviorService(m_startupBehaviorService);
m_settingsView->setTranslationService(m_translationService);
m_settingsView->setAiActionOptionsService(m_aiActionOptionsService);
m_folderModel = new FolderListModel(m_accountService, this);
qDebug() << "[MainMainWindow::connectModels] FolderListModel created";
m_emailModel = new EmailListModel(this);
+4
View File
@@ -139,6 +139,10 @@ private:
EmailListModel *m_emailModel;
AccountService *m_accountService = nullptr;
MailService *m_mailService = nullptr;
PreferencesService *m_preferencesService = nullptr;
StartupBehaviorService *m_startupBehaviorService = nullptr;
TranslationService *m_translationService = nullptr;
AiActionOptionsService *m_aiActionOptionsService = nullptr;
// Frameless title bar
QWidget *m_titleBar;
+203 -3
View File
@@ -1,6 +1,8 @@
#include "settingsview.h"
#include "services/accountservice.h"
#include "core/models/account.h"
#include "services/preferencesservice.h"
#include "services/startupbehaviorservice.h"
#include "services/translationservice.h"
#include "services/aiactionoptionsservice.h"
#include "db/dao/common_structs.h"
#include "db/dao/categorydao.h"
#include "db/dao/ruledao.h"
@@ -34,6 +36,8 @@
#include <QSqlQuery>
#include <QSqlDatabase>
#include <QHeaderView>
#include <QSpinBox>
#include <QFileDialog>
SettingsView::SettingsView(QWidget *parent) : QWidget(parent) {
setupUI();
@@ -45,6 +49,7 @@ void SettingsView::setupUI() {
m_tabWidget = new QTabWidget();
m_tabWidget->addTab(createAccountsTab(), tr("Accounts"));
m_tabWidget->addTab(createPreferencesTab(), tr("Preferences"));
m_tabWidget->addTab(createCategoriesTab(), tr("Categories"));
m_tabWidget->addTab(createRulesTab(), tr("Rules"));
m_tabWidget->addTab(createTemplatesTab(), tr("Templates"));
@@ -452,7 +457,202 @@ QWidget* SettingsView::createAppearanceTab() {
return widget;
}
// ========== Slots for Accounts ==========
QWidget* SettingsView::createPreferencesTab() {
QWidget *widget = new QWidget();
QVBoxLayout *layout = new QVBoxLayout(widget);
layout->setContentsMargins(20, 20, 20, 20);
layout->setSpacing(16);
QLabel *title = new QLabel(tr("Preferences"));
QFont titleFont = title->font();
titleFont.setPointSize(16);
titleFont.setBold(true);
title->setFont(titleFont);
layout->addWidget(title);
// Search Mode
QGroupBox *searchGroup = new QGroupBox(tr("Search"));
QFormLayout *searchLayout = new QFormLayout(searchGroup);
m_searchModeCombo = new QComboBox();
m_searchModeCombo->addItem(tr("Local"), QVariant(static_cast<int>(SearchMode::Local)));
m_searchModeCombo->addItem(tr("Online"), QVariant(static_cast<int>(SearchMode::Online)));
if (m_preferencesService) {
m_searchModeCombo->setCurrentIndex(static_cast<int>(m_preferencesService->defaultSearchMode()));
}
connect(m_searchModeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (m_preferencesService) {
m_preferencesService->setDefaultSearchMode(static_cast<SearchMode>(m_searchModeCombo->itemData(index).toInt()));
}
});
searchLayout->addRow(tr("Default search mode:"), m_searchModeCombo);
layout->addWidget(searchGroup);
// Default App Mode
QGroupBox *appModeGroup = new QGroupBox(tr("Default Application Mode"));
QFormLayout *appModeLayout = new QFormLayout(appModeGroup);
m_defaultAppModeCombo = new QComboBox();
m_defaultAppModeCombo->addItem(tr("Mail"), QVariant(static_cast<int>(WinoApplicationMode::Mail)));
m_defaultAppModeCombo->addItem(tr("Calendar"), QVariant(static_cast<int>(WinoApplicationMode::Calendar)));
m_defaultAppModeCombo->addItem(tr("Contacts"), QVariant(static_cast<int>(WinoApplicationMode::Contacts)));
m_defaultAppModeCombo->addItem(tr("Settings"), QVariant(static_cast<int>(WinoApplicationMode::Settings)));
if (m_preferencesService) {
m_defaultAppModeCombo->setCurrentIndex(static_cast<int>(m_preferencesService->defaultApplicationMode()));
}
connect(m_defaultAppModeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (m_preferencesService) {
m_preferencesService->setDefaultApplicationMode(static_cast<WinoApplicationMode>(m_defaultAppModeCombo->itemData(index).toInt()));
}
});
appModeLayout->addRow(tr("Default mode on startup:"), m_defaultAppModeCombo);
layout->addWidget(appModeGroup);
// Language
QGroupBox *languageGroup = new QGroupBox(tr("Language"));
QFormLayout *languageLayout = new QFormLayout(languageGroup);
m_languageCombo = new QComboBox();
if (m_translationService) {
QVector<AppLanguageModel> languages = m_translationService->getAvailableLanguages();
for (const AppLanguageModel &lang : languages) {
m_languageCombo->addItem(lang.displayName, lang.language);
}
int idx = m_languageCombo->findData(m_translationService->currentLanguage());
if (idx >= 0) m_languageCombo->setCurrentIndex(idx);
}
connect(m_languageCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (m_translationService) {
m_translationService->setLanguage(m_languageCombo->itemData(index).toString());
}
});
languageLayout->addRow(tr("Interface language:"), m_languageCombo);
layout->addWidget(languageGroup);
// Email Sync Interval
QGroupBox *syncGroup = new QGroupBox(tr("Email Synchronization"));
QFormLayout *syncLayout = new QFormLayout(syncGroup);
m_syncIntervalSpin = new QSpinBox();
m_syncIntervalSpin->setRange(1, 1440); // 1 minute to 24 hours
m_syncIntervalSpin->setSuffix(tr(" minutes"));
if (m_preferencesService) {
m_syncIntervalSpin->setValue(m_preferencesService->emailSyncIntervalMinutes());
}
connect(m_syncIntervalSpin, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) {
if (m_preferencesService) {
m_preferencesService->setEmailSyncIntervalMinutes(value);
}
});
syncLayout->addRow(tr("Sync interval:"), m_syncIntervalSpin);
layout->addWidget(syncGroup);
// Startup Behavior
QGroupBox *startupGroup = new QGroupBox(tr("Startup"));
QFormLayout *startupLayout = new QFormLayout(startupGroup);
m_startOnLoginCheck = new QCheckBox(tr("Start application on login"));
if (m_preferencesService) {
m_startOnLoginCheck->setChecked(m_preferencesService->startOnLogin());
}
connect(m_startOnLoginCheck, &QCheckBox::toggled, this, [this](bool checked) {
if (m_preferencesService) {
m_preferencesService->setStartOnLogin(checked);
}
if (m_startupBehaviorService) {
m_startupBehaviorService->toggleStartupBehavior(checked);
}
});
startupLayout->addRow(m_startOnLoginCheck);
m_minimizeToTrayCheck = new QCheckBox(tr("Minimize to tray on close"));
if (m_preferencesService) {
m_minimizeToTrayCheck->setChecked(m_preferencesService->minimizeToTray());
}
connect(m_minimizeToTrayCheck, &QCheckBox::toggled, this, [this](bool checked) {
if (m_preferencesService) {
m_preferencesService->setMinimizeToTray(checked);
}
});
startupLayout->addRow(m_minimizeToTrayCheck);
m_enableNotificationsCheck = new QCheckBox(tr("Enable notifications"));
if (m_preferencesService) {
m_enableNotificationsCheck->setChecked(m_preferencesService->enableNotifications());
}
connect(m_enableNotificationsCheck, &QCheckBox::toggled, this, [this](bool checked) {
if (m_preferencesService) {
m_preferencesService->setEnableNotifications(checked);
}
});
startupLayout->addRow(m_enableNotificationsCheck);
layout->addWidget(startupGroup);
// AI Translation
QGroupBox *aiGroup = new QGroupBox(tr("AI Translation"));
QFormLayout *aiLayout = new QFormLayout(aiGroup);
m_translateLanguageCombo = new QComboBox();
if (m_aiActionOptionsService) {
QVector<AiTranslateLanguageOption> options = m_aiActionOptionsService->getTranslateLanguageOptions();
for (const AiTranslateLanguageOption &opt : options) {
m_translateLanguageCombo->addItem(opt.displayName, opt.code);
}
if (m_preferencesService) {
int idx = m_translateLanguageCombo->findData(m_preferencesService->aiDefaultTranslationLanguageCode());
if (idx >= 0) m_translateLanguageCombo->setCurrentIndex(idx);
}
}
connect(m_translateLanguageCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (m_preferencesService) {
m_preferencesService->setAiDefaultTranslationLanguageCode(m_translateLanguageCombo->itemData(index).toString());
}
});
aiLayout->addRow(tr("Default translation language:"), m_translateLanguageCombo);
m_summarizeLanguageCombo = new QComboBox();
if (m_aiActionOptionsService) {
QVector<AiTranslateLanguageOption> options = m_aiActionOptionsService->getTranslateLanguageOptions();
for (const AiTranslateLanguageOption &opt : options) {
m_summarizeLanguageCombo->addItem(opt.displayName, opt.code);
}
if (m_preferencesService) {
int idx = m_summarizeLanguageCombo->findData(m_preferencesService->aiSummarizeLanguageCode());
if (idx >= 0) m_summarizeLanguageCombo->setCurrentIndex(idx);
}
}
connect(m_summarizeLanguageCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
if (m_preferencesService) {
m_preferencesService->setAiSummarizeLanguageCode(m_summarizeLanguageCombo->itemData(index).toString());
}
});
aiLayout->addRow(tr("Summarize language:"), m_summarizeLanguageCombo);
m_summarySavePathEdit = new QLineEdit();
m_summarySavePathEdit->setPlaceholderText(tr("Select folder for AI summaries..."));
if (m_preferencesService) {
m_summarySavePathEdit->setText(m_preferencesService->aiSummarySavePath());
}
m_summarySavePathBtn = new QPushButton(tr("Browse..."));
connect(m_summarySavePathBtn, &QPushButton::clicked, this, [this]() {
QString path = QFileDialog::getExistingDirectory(this, tr("Select Summary Save Folder"),
m_summarySavePathEdit->text());
if (!path.isEmpty()) {
m_summarySavePathEdit->setText(path);
if (m_preferencesService) {
m_preferencesService->setAiSummarySavePath(path);
}
}
});
QHBoxLayout *pathLayout = new QHBoxLayout();
pathLayout->addWidget(m_summarySavePathEdit);
pathLayout->addWidget(m_summarySavePathBtn);
aiLayout->addRow(tr("Summary save path:"), pathLayout);
layout->addWidget(aiGroup);
layout->addStretch();
return widget;
}
void SettingsView::setAccountService(AccountService *service) {
m_accountService = service;
if (m_accountService) {
+31
View File
@@ -17,6 +17,10 @@
#include <QDialog>
#include <QColorDialog>
#include "services/accountservice.h"
#include "services/preferencesservice.h"
#include "services/startupbehaviorservice.h"
#include "services/translationservice.h"
#include "services/aiactionoptionsservice.h"
#include "db/dao/common_structs.h"
class SettingsView : public QWidget {
@@ -27,6 +31,10 @@ public:
~SettingsView() override = default;
void setAccountService(AccountService *service);
void setPreferencesService(PreferencesService *service);
void setStartupBehaviorService(StartupBehaviorService *service);
void setTranslationService(TranslationService *service);
void setAiActionOptionsService(AiActionOptionsService *service);
signals:
void accountAddRequested();
@@ -92,6 +100,7 @@ private:
QWidget* createAccountsTab();
QWidget* createGeneralTab();
QWidget* createAppearanceTab();
QWidget* createPreferencesTab(); // New: Preferences tab
QWidget* createCategoriesTab();
QWidget* createRulesTab();
QWidget* createTemplatesTab();
@@ -149,4 +158,26 @@ private:
QPushButton *m_signatureAddBtn;
QPushButton *m_signatureEditBtn;
QPushButton *m_signatureDeleteBtn;
// Services
AccountService *m_accountService = nullptr;
PreferencesService *m_preferencesService = nullptr;
StartupBehaviorService *m_startupBehaviorService = nullptr;
TranslationService *m_translationService = nullptr;
AiActionOptionsService *m_aiActionOptionsService = nullptr;
// Preferences tab controls
QComboBox *m_searchModeCombo = nullptr;
QComboBox *m_defaultAppModeCombo = nullptr;
QComboBox *m_languageCombo = nullptr;
QSpinBox *m_syncIntervalSpin = nullptr;
QCheckBox *m_startOnLoginCheck = nullptr;
QCheckBox *m_minimizeToTrayCheck = nullptr;
QCheckBox *m_enableNotificationsCheck = nullptr;
QComboBox *m_translateLanguageCombo = nullptr;
QComboBox *m_summarizeLanguageCombo = nullptr;
QLineEdit *m_summarySavePathEdit = nullptr;
QPushButton *m_summarySavePathBtn = nullptr;
int m_selectedAccountId = -1;
};