Actualizaciones varias: mejoras en cuenta, sincronización, UI
This commit is contained in:
@@ -5,14 +5,17 @@
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionValidator>
|
||||
#include <QIntValidator>
|
||||
#include <QTimer>
|
||||
|
||||
// ─────────────────────── Constructor ───────────────────────
|
||||
|
||||
AccountSetupDialog::AccountSetupDialog(AccountService *accountService, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_accountService(accountService)
|
||||
, m_selectedProvider(0)
|
||||
, m_accountCreatedOk(false)
|
||||
, m_editingAccountId(-1)
|
||||
, m_isEditing(false)
|
||||
{
|
||||
m_authTimeoutTimer = new QTimer(this);
|
||||
m_authTimeoutTimer->setSingleShot(true);
|
||||
@@ -26,8 +29,44 @@ AccountSetupDialog::AccountSetupDialog(AccountService *accountService, QWidget *
|
||||
setupUI();
|
||||
}
|
||||
|
||||
// ─────────────────────── UI Setup ───────────────────────
|
||||
// ─────────────────────── Public API ───────────────────────
|
||||
void AccountSetupDialog::loadAccountForEditing(const Account &account)
|
||||
{
|
||||
// Only support editing IMAP accounts for simplicity
|
||||
if (account.type() != AccountType::IMAP) {
|
||||
QMessageBox::information(this, tr("Edit Account"),
|
||||
tr("Editing of OAuth (Gmail/Outlook) accounts is not supported in this version.\n"
|
||||
"You can add a new account instead."));
|
||||
m_isEditing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_isEditing = true;
|
||||
m_editingAccountId = account.id();
|
||||
|
||||
// Set window title
|
||||
setWindowTitle(tr("Edit Email Account – Wino Mail"));
|
||||
|
||||
// Fill IMAP fields
|
||||
m_imapEmailEdit->setText(account.email());
|
||||
m_imapNameEdit->setText(account.displayName());
|
||||
|
||||
// Password field left blank for security; user must re-enter
|
||||
m_imapPasswordEdit->clear();
|
||||
|
||||
const Account::ConnectionSettings &settings = account.connectionSettings();
|
||||
m_imapHostEdit->setText(settings.incomingHost);
|
||||
m_imapPortEdit->setText(QString::number(settings.incomingPort));
|
||||
m_smtpHostEdit->setText(settings.outgoingHost);
|
||||
m_smtpPortEdit->setText(QString::number(settings.outgoingPort));
|
||||
m_sslCheckbox->setChecked(settings.incomingSsl); // assuming same for SMTP
|
||||
|
||||
// Switch to IMAP page
|
||||
m_selectedProvider = 2; // IMAP
|
||||
goToPage(PageImap);
|
||||
}
|
||||
|
||||
// ─────────────────────── UI Setup ───────────────────────
|
||||
void AccountSetupDialog::setupUI()
|
||||
{
|
||||
setWindowTitle("Añadir Cuenta de Correo — Wino Mail");
|
||||
@@ -105,7 +144,6 @@ void AccountSetupDialog::setupUI()
|
||||
}
|
||||
|
||||
// ─────────────────── Page 0: Provider ───────────────────
|
||||
|
||||
QWidget* AccountSetupDialog::createProviderPage()
|
||||
{
|
||||
QWidget *page = new QWidget();
|
||||
@@ -165,7 +203,6 @@ QWidget* AccountSetupDialog::createProviderPage()
|
||||
}
|
||||
|
||||
// ─────────────────── Page 1: OAuth ───────────────────
|
||||
|
||||
QWidget* AccountSetupDialog::createOAuthPage()
|
||||
{
|
||||
QWidget *page = new QWidget();
|
||||
@@ -178,12 +215,12 @@ QWidget* AccountSetupDialog::createOAuthPage()
|
||||
lay->addWidget(title);
|
||||
|
||||
QLabel *info = new QLabel(
|
||||
"Se abrirá tu navegador web para que inicies sesión de forma segura.\n"
|
||||
"Wino Mail no almacena tu contraseña, solo el token de acceso autorizado.\n\n"
|
||||
"Pasos:\n"
|
||||
" 1. Introduce tu dirección de correo.\n"
|
||||
" 2. Pulsa «Autenticar en Navegador».\n"
|
||||
" 3. Completa el inicio de sesión en la ventana del navegador.\n"
|
||||
"Se abrirá tu navegador web para que inicies sesión de forma segura.\\n"
|
||||
"Wino Mail no almacena tu contraseña, solo el token de acceso autorizado.\\n\\n"
|
||||
"Pasos:\\n"
|
||||
" 1. Introduce tu dirección de correo.\\n"
|
||||
" 2. Pulsa «Autenticar en Navegador».\\n"
|
||||
" 3. Completa el inicio de sesión en la ventana del navegador.\\n"
|
||||
" 4. Al terminar, esta ventana se actualizará automáticamente."
|
||||
);
|
||||
info->setWordWrap(true);
|
||||
@@ -224,7 +261,6 @@ QWidget* AccountSetupDialog::createOAuthPage()
|
||||
}
|
||||
|
||||
// ─────────────────── Page 2: IMAP ───────────────────
|
||||
|
||||
QWidget* AccountSetupDialog::createImapPage()
|
||||
{
|
||||
QWidget *page = new QWidget();
|
||||
@@ -308,7 +344,6 @@ QWidget* AccountSetupDialog::createImapPage()
|
||||
}
|
||||
|
||||
// ─────────────────── Page 3: Progress ───────────────────
|
||||
|
||||
QWidget* AccountSetupDialog::createProgressPage()
|
||||
{
|
||||
QWidget *page = new QWidget();
|
||||
@@ -340,7 +375,6 @@ QWidget* AccountSetupDialog::createProgressPage()
|
||||
}
|
||||
|
||||
// ─────────────────── Navigation ───────────────────
|
||||
|
||||
void AccountSetupDialog::goToPage(int page)
|
||||
{
|
||||
m_stack->setCurrentIndex(page);
|
||||
@@ -364,7 +398,7 @@ void AccountSetupDialog::updateNavButtons()
|
||||
m_btnNext->setVisible(false); // OAuth flow is driven by the authenticate button
|
||||
break;
|
||||
case PageImap:
|
||||
m_btnNext->setText("Conectar");
|
||||
m_btnNext->setText(m_isEditing ? "Guardar cambios" : "Conectar");
|
||||
m_btnNext->setEnabled(true);
|
||||
m_btnNext->setVisible(true);
|
||||
break;
|
||||
@@ -423,7 +457,6 @@ void AccountSetupDialog::onCancelClicked()
|
||||
}
|
||||
|
||||
// ─────────────────── OAuth Flow ───────────────────
|
||||
|
||||
void AccountSetupDialog::startOAuthAuthentication()
|
||||
{
|
||||
QString email = m_oauthEmailEdit->text().trimmed();
|
||||
@@ -458,7 +491,6 @@ void AccountSetupDialog::onAuthTimeout()
|
||||
}
|
||||
|
||||
// ─────────────────── IMAP Flow ───────────────────
|
||||
|
||||
void AccountSetupDialog::submitImapAccount()
|
||||
{
|
||||
QString email = m_imapEmailEdit->text().trimmed();
|
||||
@@ -489,20 +521,45 @@ void AccountSetupDialog::submitImapAccount()
|
||||
if (smtpHost.isEmpty()) smtpHost = imapHost.replace("imap.", "smtp.");
|
||||
if (name.isEmpty()) name = email.split("@").first();
|
||||
|
||||
// Create account with connection settings
|
||||
Account account;
|
||||
account.setEmail(email);
|
||||
account.setDisplayName(name);
|
||||
account.setType(AccountType::IMAP);
|
||||
|
||||
Account::ConnectionSettings settings;
|
||||
settings.type = "imap";
|
||||
settings.incomingHost = imapHost;
|
||||
settings.incomingPort = imapPort.toInt();
|
||||
settings.incomingSsl = m_sslCheckbox->isChecked();
|
||||
settings.outgoingHost = smtpHost;
|
||||
settings.outgoingPort = smtpPort.toInt();
|
||||
settings.outgoingSsl = m_sslCheckbox->isChecked();
|
||||
settings.username = email;
|
||||
settings.password = password;
|
||||
settings.authMethod = "plain";
|
||||
|
||||
account.setConnectionSettings(settings);
|
||||
|
||||
// Show progress
|
||||
m_accountCreatedOk = false;
|
||||
goToPage(PageProgress);
|
||||
m_progressIcon->setText("⏳");
|
||||
m_progressText->setText("Conectando al servidor…");
|
||||
m_progressText->setText("Conectando al servidor...");
|
||||
m_progressDetail->setText(
|
||||
QString("Servidor IMAP: %1:%2\nServidor SMTP: %3:%4\nSSL: %5")
|
||||
QString("Servidor IMAP: %1:%2\\nServidor SMTP: %3:%4\\nSSL: %5")
|
||||
.arg(imapHost, imapPort, smtpHost, smtpPort,
|
||||
m_sslCheckbox->isChecked() ? "Sí" : "No")
|
||||
);
|
||||
|
||||
// Simulate connection delay, then add account
|
||||
QTimer::singleShot(1500, this, [this, email, name, password]() {
|
||||
m_accountService->addAccount(email, "imap", password, "");
|
||||
// Simulate connection delay, then add/update account
|
||||
QTimer::singleShot(1500, this, [this, account]() mutable {
|
||||
if (m_isEditing) {
|
||||
account.setId(m_editingAccountId);
|
||||
m_accountService->updateAccount(account);
|
||||
} else {
|
||||
m_accountService->addAccount(account);
|
||||
}
|
||||
});
|
||||
|
||||
// Timeout for IMAP too
|
||||
@@ -510,21 +567,22 @@ void AccountSetupDialog::submitImapAccount()
|
||||
}
|
||||
|
||||
// ─────────────────── Account Result ───────────────────
|
||||
|
||||
void AccountSetupDialog::onAccountAdded(const Account &account)
|
||||
{
|
||||
m_authTimeoutTimer->stop();
|
||||
m_accountCreatedOk = true;
|
||||
|
||||
qDebug() << "[AccountSetupDialog] Account created successfully:" << account.email();
|
||||
qDebug() << "[AccountSetupDialog] Account" << (m_isEditing ? "updated" : "created") << "successfully:" << account.email();
|
||||
|
||||
// If we were on the OAuth page, move to progress first
|
||||
if (m_stack->currentIndex() == PageOAuth) {
|
||||
goToPage(PageProgress);
|
||||
}
|
||||
|
||||
showSuccess(QString("¡Cuenta «%1» configurada con éxito!\n\n"
|
||||
"La sincronización de correos comenzará automáticamente en segundo plano.")
|
||||
showSuccess(QString(m_isEditing ? "¡Cuenta «%1» actualizada con éxito!\\n\\n"
|
||||
"Los cambios se aplicarán inmediatamente."
|
||||
: "¡Cuenta «%1» configurada con éxito!\\n\\n"
|
||||
"La sincronización de correos comenzará automáticamente en segundo plano.")
|
||||
.arg(account.email()));
|
||||
|
||||
emit accountCreated(account);
|
||||
@@ -551,4 +609,4 @@ void AccountSetupDialog::showError(const QString &message)
|
||||
m_btnNext->setVisible(false);
|
||||
}
|
||||
|
||||
#include "accountsetupdialog.moc"
|
||||
#include "accountsetupdialog.moc"
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <QFormLayout>
|
||||
#include <QIntValidator>
|
||||
#include <QTimer>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionValidator>
|
||||
#include "core/models/account.h"
|
||||
#include "services/accountservice.h"
|
||||
|
||||
@@ -24,6 +26,9 @@ public:
|
||||
explicit AccountSetupDialog(AccountService *accountService, QWidget *parent = nullptr);
|
||||
~AccountSetupDialog() override = default;
|
||||
|
||||
/// Load an existing account for editing (currently only IMAP accounts supported)
|
||||
void loadAccountForEditing(const Account &account);
|
||||
|
||||
signals:
|
||||
void accountCreated(const Account &account);
|
||||
|
||||
@@ -91,6 +96,10 @@ private:
|
||||
|
||||
int m_selectedProvider; // 0=Gmail, 1=Outlook, 2=IMAP
|
||||
bool m_accountCreatedOk;
|
||||
|
||||
// Editing state
|
||||
int m_editingAccountId = -1;
|
||||
bool m_isEditing = false;
|
||||
};
|
||||
|
||||
#endif // ACCOUNTSETUPDIALOG_H
|
||||
#endif // ACCOUNTSETUPDIALOG_H
|
||||
+145
-18
@@ -60,22 +60,22 @@ void RichTextEditor::setupToolbar(QVBoxLayout *layout) {
|
||||
m_toolbar->addSeparator();
|
||||
|
||||
// Alignment
|
||||
QAction *alignLeft = m_toolbar->addAction("\\xe2\\x87\\x94L");
|
||||
QAction *alignLeft = m_toolbar->addAction("L");
|
||||
connect(alignLeft, &QAction::triggered, this, &RichTextEditor::onAlignLeft);
|
||||
|
||||
QAction *alignCenter = m_toolbar->addAction("\\xe2\\x86\\x94C");
|
||||
QAction *alignCenter = m_toolbar->addAction("C");
|
||||
connect(alignCenter, &QAction::triggered, this, &RichTextEditor::onAlignCenter);
|
||||
|
||||
QAction *alignRight = m_toolbar->addAction("\\xe2\\x87\\x94R");
|
||||
QAction *alignRight = m_toolbar->addAction("R");
|
||||
connect(alignRight, &QAction::triggered, this, &RichTextEditor::onAlignRight);
|
||||
|
||||
QAction *alignJustify = m_toolbar->addAction("\\xe2\\x87\\x94J");
|
||||
QAction *alignJustify = m_toolbar->addAction("J");
|
||||
connect(alignJustify, &QAction::triggered, this, &RichTextEditor::onAlignJustify);
|
||||
|
||||
m_toolbar->addSeparator();
|
||||
|
||||
// Lists
|
||||
QAction *bulletAct = m_toolbar->addAction("\\xe2\\x80\\xa2 List");
|
||||
QAction *bulletAct = m_toolbar->addAction("Bullets");
|
||||
connect(bulletAct, &QAction::triggered, this, &RichTextEditor::onBulletList);
|
||||
|
||||
QAction *numAct = m_toolbar->addAction("1. List");
|
||||
@@ -84,23 +84,40 @@ void RichTextEditor::setupToolbar(QVBoxLayout *layout) {
|
||||
m_toolbar->addSeparator();
|
||||
|
||||
// Indent / Outdent
|
||||
QAction *indentAct = m_toolbar->addAction("\\xe2\\x86\\x92 Indent");
|
||||
QAction *indentAct = m_toolbar->addAction("Indent");
|
||||
connect(indentAct, &QAction::triggered, this, &RichTextEditor::onIndent);
|
||||
|
||||
QAction *outdentAct = m_toolbar->addAction("\\xe2\\x86\\x90 Outdent");
|
||||
QAction *outdentAct = m_toolbar->addAction("Outdent");
|
||||
connect(outdentAct, &QAction::triggered, this, &RichTextEditor::onOutdent);
|
||||
|
||||
m_toolbar->addSeparator();
|
||||
|
||||
// Insert image
|
||||
QAction *imgAct = m_toolbar->addAction("\\xf0\\x9f\\x96\\xbc Image");
|
||||
connect(imgAct, &QAction::triggered, this, &RichTextEditor::onInsertImage);
|
||||
QAction *imgAct = m_toolbar->addAction("Img");
|
||||
connect(imgAct, &QAction::triggered, this, &RichTextEditor::onInsertImage);
|
||||
|
||||
// Insert table
|
||||
QAction *tableAct = m_toolbar->addAction("\\xe2\\x96\\xa4 Table");
|
||||
connect(tableAct, &QAction::triggered, this, &RichTextEditor::onInsertTable);
|
||||
// Insert table
|
||||
QAction *tableAct = m_toolbar->addAction("Tbl");
|
||||
connect(tableAct, &QAction::triggered, this, &RichTextEditor::onInsertTable);
|
||||
|
||||
layout->addWidget(m_toolbar);
|
||||
// Signature button
|
||||
m_signatureButton = new QToolButton();
|
||||
m_signatureButton->setText("Signature");
|
||||
m_signatureButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
m_signatureButton->setPopupMode(QToolButton::MenuButtonPopup);
|
||||
m_signatureMenu = new QMenu(m_signatureButton);
|
||||
m_signatureButton->setMenu(m_signatureMenu);
|
||||
m_signatureButton->setStyleSheet(
|
||||
"QToolButton { background: #f5f5f7; border: 1px solid #d1d1d6; border-radius: 3px; padding: 4px 6px; }"
|
||||
"QToolButton:hover { background: #e0e0e0; }"
|
||||
"QToolButton::menu-button { border-left: 1px solid rgba(0,0,0,0.1); width: 12px; }"
|
||||
);
|
||||
connect(m_signatureButton, &QToolButton::clicked, this, &RichTextEditor::signatureClicked);
|
||||
m_toolbar->addWidget(m_signatureButton);
|
||||
|
||||
m_toolbar->addSeparator();
|
||||
|
||||
layout->addWidget(m_toolbar);
|
||||
}
|
||||
|
||||
void RichTextEditor::onBold() {
|
||||
@@ -244,12 +261,99 @@ void RichTextEditor::onInsertTable() {
|
||||
cursor.insertTable(rows, cols, tableFmt);
|
||||
}
|
||||
|
||||
// ===================== ComposeView =====================
|
||||
|
||||
ComposeView::ComposeView(QWidget *parent) : QWidget(parent) {
|
||||
setupUI();
|
||||
// Connect the rich text editor's signature signal to our slot
|
||||
connect(m_bodyEditor, &RichTextEditor::signatureClicked, this, &ComposeView::onSignatureClicked);
|
||||
}
|
||||
|
||||
ComposeView::~ComposeView() {
|
||||
// Destructor implementation
|
||||
}
|
||||
void ComposeView::onSignatureClicked() {
|
||||
// This slot is called when the signature button in the rich text editor is clicked
|
||||
// The actual menu handling is done in RichTextEditor, so we just need to
|
||||
// handle any ComposeView-specific logic here if needed
|
||||
}
|
||||
|
||||
void ComposeView::onSignatureEditRequested() {
|
||||
// This slot will be connected to the "Edit Signature..." action in the menu
|
||||
// For now, we'll just show a simple input dialog
|
||||
QString currentSignature = "";
|
||||
// In a real implementation, we would get the current signature for the selected account
|
||||
// For now, we'll use an empty string
|
||||
|
||||
bool ok;
|
||||
QString newSignature = QInputDialog::getMultiLineText(this, "Edit Signature",
|
||||
"Enter your signature:",
|
||||
currentSignature, &ok);
|
||||
if (ok && !newSignature.isEmpty()) {
|
||||
// In a real implementation, we would save this signature for the selected account
|
||||
// For now, we'll just insert it at the cursor position
|
||||
QTextCursor cursor = m_bodyEditor->textCursor();
|
||||
cursor.insertHtml(newSignature);
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeView::setAccountService(AccountService *service)
|
||||
{
|
||||
m_accountService = service;
|
||||
populateAccountCombo();
|
||||
}
|
||||
|
||||
void ComposeView::populateAccountCombo()
|
||||
{
|
||||
if (!m_accountService) {
|
||||
qWarning() << "AccountService not set";
|
||||
return;
|
||||
}
|
||||
|
||||
m_accountCombo->clear();
|
||||
m_accountCombo->addItem(tr("Select Account..."), QVariant());
|
||||
|
||||
QVector<Account> accounts = m_accountService->getAllAccounts();
|
||||
for (const Account &account : accounts) {
|
||||
m_accountCombo->addItem(account.email(), account.id());
|
||||
}
|
||||
|
||||
// Select the first account by default if there are accounts
|
||||
if (accounts.size() > 0) {
|
||||
m_accountCombo->setCurrentIndex(1); // Skip the placeholder item
|
||||
m_currentAccountId = accounts.first().id();
|
||||
}
|
||||
}
|
||||
|
||||
void ComposeView::onAccountChanged(int index)
|
||||
{
|
||||
if (index <= 0) {
|
||||
// Placeholder item selected or invalid index
|
||||
m_currentAccountId = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentAccountId = m_accountCombo->itemData(index).toInt();
|
||||
loadSignatureForCurrentAccount();
|
||||
}
|
||||
|
||||
void ComposeView::loadSignatureForCurrentAccount()
|
||||
{
|
||||
if (!m_accountService || m_currentAccountId <= 0) {
|
||||
// Clear signature if no account selected
|
||||
m_bodyEditor->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Account* account = m_accountService->findAccountById(m_currentAccountId);
|
||||
if (account) {
|
||||
// For now, we'll just clear the editor since we don't have a signature field in Account yet
|
||||
// In a real implementation, we would retrieve and display the signature
|
||||
m_bodyEditor->clear();
|
||||
} else {
|
||||
m_bodyEditor->clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ComposeView::setupUI() {
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
||||
mainLayout->setContentsMargins(20, 15, 20, 15);
|
||||
@@ -271,7 +375,7 @@ void ComposeView::setupUI() {
|
||||
m_subjectField->setFont(subjectFont);
|
||||
headerLayout->addWidget(m_subjectField, 1);
|
||||
|
||||
m_detachButton = new QPushButton("\\xe2\\x87\\xa5 Detach");
|
||||
m_detachButton = new QPushButton("\xe2\x87\xa5 Detach");
|
||||
m_detachButton->setToolTip("Open compose window in a separate window");
|
||||
m_detachButton->setStyleSheet(
|
||||
"QPushButton { background: transparent; border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 12px; color: #555; font-size: 12px; }"
|
||||
@@ -288,6 +392,27 @@ void ComposeView::setupUI() {
|
||||
line1->setStyleSheet("color: #e0e0e0;");
|
||||
mainLayout->addWidget(line1);
|
||||
|
||||
// From: field + Account selector
|
||||
QHBoxLayout *fromLayout = new QHBoxLayout();
|
||||
QLabel *fromLabel = new QLabel("From:");
|
||||
fromLabel->setFixedWidth(40);
|
||||
fromLabel->setStyleSheet("font-weight: bold; color: #555;");
|
||||
m_accountCombo = new QComboBox();
|
||||
m_accountCombo->setFixedWidth(180);
|
||||
m_accountCombo->setStyleSheet(
|
||||
"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 6px 8px; "
|
||||
"background: white; min-height: 20px; }"
|
||||
"QComboBox:hover { border-color: #bdbdbd; }"
|
||||
"QComboBox:focus { border-color: #1976D2; }"
|
||||
"QComboBox::drop-down { border: none; width: 20px; }"
|
||||
"QComboBox::down-arrow { image: url(:/icons/dropdown.png); width: 10px; height: 10px; }"
|
||||
);
|
||||
connect(m_accountCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &ComposeView::onAccountChanged);
|
||||
fromLayout->addWidget(fromLabel);
|
||||
fromLayout->addWidget(m_accountCombo, 1);
|
||||
mainLayout->addLayout(fromLayout);
|
||||
|
||||
// To: field + Cc/Bcc toggle buttons
|
||||
QHBoxLayout *toLayout = new QHBoxLayout();
|
||||
QLabel *toLabel = new QLabel("To:");
|
||||
@@ -462,7 +587,8 @@ void ComposeView::onSendClicked() {
|
||||
m_bccVisible ? m_bccField->text() : QString(),
|
||||
m_subjectField->text(),
|
||||
m_bodyEditor->toHtml(),
|
||||
QDateTime() // null = send now
|
||||
QDateTime(), // null = send now
|
||||
m_currentAccountId > 0 ? m_accountCombo->itemData(m_accountCombo->currentIndex()).toString() : QString()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -473,7 +599,8 @@ void ComposeView::onScheduleClicked() {
|
||||
m_bccVisible ? m_bccField->text() : QString(),
|
||||
m_subjectField->text(),
|
||||
m_bodyEditor->toHtml(),
|
||||
m_schedulePicker->dateTime()
|
||||
m_schedulePicker->dateTime(),
|
||||
m_currentAccountId > 0 ? m_accountCombo->itemData(m_accountCombo->currentIndex()).toString() : QString()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+27
-4
@@ -1,5 +1,5 @@
|
||||
#ifndef COMPOSEVIEW_H
|
||||
#define COMPOSEVIEW_H
|
||||
#ifndef COMPOSE_VIEW_H
|
||||
#define COMPOSE_VIEW_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTextEdit>
|
||||
@@ -15,7 +15,11 @@
|
||||
#include <QDateTimeEdit>
|
||||
#include <QFontComboBox>
|
||||
#include <QSpinBox>
|
||||
#include <QComboBox>
|
||||
#include "models/EmailCompositionModel.h"
|
||||
#include "services/accountservice.h"
|
||||
|
||||
class AccountService;
|
||||
|
||||
class RichTextEditor : public QTextEdit {
|
||||
Q_OBJECT
|
||||
@@ -23,6 +27,10 @@ public:
|
||||
explicit RichTextEditor(QWidget *parent = nullptr);
|
||||
void setupToolbar(QVBoxLayout *layout);
|
||||
|
||||
signals:
|
||||
void signatureClicked();
|
||||
void signatureEditRequested();
|
||||
|
||||
public slots:
|
||||
void onBold();
|
||||
void onItalic();
|
||||
@@ -44,19 +52,25 @@ private:
|
||||
QToolBar *m_toolbar;
|
||||
QFontComboBox *m_fontCombo;
|
||||
QSpinBox *m_fontSizeSpin;
|
||||
QToolButton *m_signatureButton;
|
||||
QMenu *m_signatureMenu;
|
||||
};
|
||||
|
||||
class ComposeView : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ComposeView(QWidget *parent = nullptr);
|
||||
~ComposeView() override;
|
||||
|
||||
void setAccountService(AccountService *service);
|
||||
|
||||
signals:
|
||||
void compositionFinished();
|
||||
void detachRequested(QWidget *widget);
|
||||
void sendRequested(const QString &to, const QString &cc, const QString &bcc,
|
||||
const QString &subject, const QString &body,
|
||||
const QDateTime &scheduleTime);
|
||||
const QDateTime &scheduleTime,
|
||||
const QString &fromAddress);
|
||||
void discardRequested();
|
||||
|
||||
public slots:
|
||||
@@ -72,10 +86,17 @@ private slots:
|
||||
void onSendClicked();
|
||||
void onScheduleClicked();
|
||||
void onDetachClicked();
|
||||
void onAccountChanged(int index);
|
||||
void onSignatureClicked();
|
||||
void onSignatureEditRequested();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void populateAccountCombo();
|
||||
void loadSignatureForCurrentAccount();
|
||||
|
||||
EmailCompositionModel *m_compositionModel;
|
||||
AccountService *m_accountService = nullptr;
|
||||
// UI components
|
||||
QLineEdit *m_toField = nullptr;
|
||||
QLineEdit *m_subjectField = nullptr;
|
||||
@@ -97,8 +118,10 @@ private:
|
||||
QAction *m_sendNowAction = nullptr;
|
||||
QAction *m_scheduleAction = nullptr;
|
||||
QToolButton *m_sendSplit = nullptr;
|
||||
QComboBox *m_accountCombo = nullptr;
|
||||
bool m_ccVisible = false;
|
||||
bool m_bccVisible = false;
|
||||
int m_currentAccountId = -1;
|
||||
};
|
||||
|
||||
#endif // COMPOSEVIEW_H
|
||||
#endif // COMPOSE_VIEW_H
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
~MailListView() override = default;
|
||||
|
||||
void setModel(EmailListModel *model);
|
||||
QTableView* tableView() const { return m_tableView; }
|
||||
|
||||
signals:
|
||||
void emailSelected(int mailId);
|
||||
|
||||
+91
-21
@@ -19,9 +19,20 @@ MainMainWindow::MainMainWindow(QWidget *parent)
|
||||
connectModels();
|
||||
setWindowTitle("Wino Mail DTK");
|
||||
resize(1280, 820);
|
||||
// Setup progress bar in status bar
|
||||
m_progressBar = new QProgressBar(this);
|
||||
m_progressBar->setMaximumWidth(120);
|
||||
m_progressBar->setVisible(false);
|
||||
statusBar()->addPermanentWidget(m_progressBar);
|
||||
// Connect mail service progress/status signals
|
||||
connect(m_mailService, &MailService::progressChanged, this, &MainMainWindow::onProgressChanged);
|
||||
connect(m_mailService, &MailService::statusMessage, this, &MainMainWindow::onStatusMessage);
|
||||
// Provide account service to settings view
|
||||
m_settingsView->setAccountService(m_accountService);
|
||||
}
|
||||
|
||||
void MainMainWindow::setupUI() {
|
||||
void MainMainWindow::setupUI()
|
||||
{
|
||||
// === Global stylesheet ===
|
||||
this->setStyleSheet(
|
||||
"QMainWindow { background-color: #f5f5f7; }"
|
||||
@@ -58,7 +69,7 @@ void MainMainWindow::setupUI() {
|
||||
|
||||
// Page 1: Compose
|
||||
m_composeView = new ComposeView();
|
||||
connect(m_composeView, &ComposeView::sendRequested, [this](const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QDateTime &scheduleTime) {
|
||||
connect(m_composeView, &ComposeView::sendRequested, [this](const QString &to, const QString &cc, const QString &bcc, const QString &subject, const QString &body, const QDateTime &scheduleTime, const QString &fromAddr) {
|
||||
QString msg;
|
||||
if (scheduleTime.isValid()) {
|
||||
msg = QString("Message scheduled for: %1").arg(scheduleTime.toString("dd/MM/yyyy hh:mm AP"));
|
||||
@@ -83,16 +94,16 @@ void MainMainWindow::setupUI() {
|
||||
// Create standalone window
|
||||
QMainWindow *detachedWin = new QMainWindow();
|
||||
detachedWin->setWindowTitle("Compose - Wino Mail");
|
||||
|
||||
|
||||
// Assign central widget and ensure it's visible and sized
|
||||
detachedWin->setCentralWidget(composeView);
|
||||
composeView->setMinimumSize(800, 600);
|
||||
composeView->update();
|
||||
|
||||
|
||||
detachedWin->resize(800, 600);
|
||||
detachedWin->setAttribute(Qt::WA_DeleteOnClose);
|
||||
detachedWin->show();
|
||||
|
||||
|
||||
statusBar()->showMessage("Compose view detached to separate window", 3000);
|
||||
});
|
||||
m_stack->addWidget(m_composeView);
|
||||
@@ -103,6 +114,8 @@ void MainMainWindow::setupUI() {
|
||||
AccountSetupDialog dlg(m_accountService, this);
|
||||
dlg.exec();
|
||||
});
|
||||
connect(m_settingsView, &SettingsView::accountEditRequested, this, &MainMainWindow::onAccountEditRequested);
|
||||
connect(m_settingsView, &SettingsView::accountDeleteRequested, this, &MainMainWindow::onAccountDeleteRequested);
|
||||
connect(m_settingsView, &SettingsView::themeChanged, [this](const QString &theme) {
|
||||
statusBar()->showMessage(QString("Theme changed to: %1 (restart may be required)").arg(theme), 3000);
|
||||
});
|
||||
@@ -123,7 +136,8 @@ void MainMainWindow::setupUI() {
|
||||
switchToPage(PageMail);
|
||||
}
|
||||
|
||||
void MainMainWindow::setupSidebar() {
|
||||
void MainMainWindow::setupSidebar()
|
||||
{
|
||||
m_sidebar = new QListWidget();
|
||||
m_sidebar->setFixedWidth(64);
|
||||
m_sidebar->setIconSize(QSize(24, 24));
|
||||
@@ -146,7 +160,8 @@ void MainMainWindow::setupSidebar() {
|
||||
connect(m_sidebar, &QListWidget::currentRowChanged, this, &MainMainWindow::onNavChanged);
|
||||
}
|
||||
|
||||
void MainMainWindow::setupMailPage() {
|
||||
void MainMainWindow::setupMailPage()
|
||||
{
|
||||
m_mailPage = new QWidget();
|
||||
QHBoxLayout *mailLayout = new QHBoxLayout(m_mailPage);
|
||||
mailLayout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -191,9 +206,11 @@ void MainMainWindow::setupMailPage() {
|
||||
mailLayout->addWidget(m_folderSplitter);
|
||||
}
|
||||
|
||||
void MainMainWindow::connectModels() {
|
||||
void MainMainWindow::connectModels()
|
||||
{
|
||||
m_accountService = new AccountService(this);
|
||||
m_mailService = new MailService(this);
|
||||
m_mailService = new MailService(m_accountService, this);
|
||||
m_composeView->setAccountService(m_accountService);
|
||||
|
||||
m_folderModel = new FolderListModel(m_accountService, this);
|
||||
m_emailModel = new EmailListModel(this);
|
||||
@@ -206,11 +223,13 @@ void MainMainWindow::connectModels() {
|
||||
connect(m_folderTree, &QTreeView::clicked, this, &MainMainWindow::onFolderSelected);
|
||||
}
|
||||
|
||||
void MainMainWindow::onNavChanged(int index) {
|
||||
void MainMainWindow::onNavChanged(int index)
|
||||
{
|
||||
switchToPage(static_cast<Page>(index));
|
||||
}
|
||||
|
||||
void MainMainWindow::switchToPage(int pageIndex) {
|
||||
void MainMainWindow::switchToPage(int pageIndex)
|
||||
{
|
||||
m_stack->setCurrentIndex(pageIndex);
|
||||
m_sidebar->blockSignals(true);
|
||||
m_sidebar->setCurrentRow(pageIndex);
|
||||
@@ -219,7 +238,8 @@ void MainMainWindow::switchToPage(int pageIndex) {
|
||||
// Show/hide toolbar actions per page
|
||||
}
|
||||
|
||||
void MainMainWindow::onFolderSelected(const QModelIndex &index) {
|
||||
void MainMainWindow::onFolderSelected(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid()) return;
|
||||
|
||||
int itemType = index.data(FolderListModel::ItemTypeRole).toInt();
|
||||
@@ -240,7 +260,8 @@ void MainMainWindow::onFolderSelected(const QModelIndex &index) {
|
||||
}
|
||||
}
|
||||
|
||||
void MainMainWindow::onEmailSelected(int mailId) {
|
||||
void MainMainWindow::onEmailSelected(int mailId)
|
||||
{
|
||||
m_currentMailId = mailId;
|
||||
std::optional<MailItem> item = MailItemDao::findById(mailId);
|
||||
if (!item.has_value()) {
|
||||
@@ -256,11 +277,13 @@ void MainMainWindow::onEmailSelected(int mailId) {
|
||||
m_emailViewer->setMailItem(&mail);
|
||||
}
|
||||
|
||||
void MainMainWindow::onComposeRequested() {
|
||||
void MainMainWindow::onComposeRequested()
|
||||
{
|
||||
switchToPage(PageCompose);
|
||||
}
|
||||
|
||||
void MainMainWindow::onReaderReplyRequested(const MailItem *item) {
|
||||
void MainMainWindow::onReaderReplyRequested(const MailItem *item)
|
||||
{
|
||||
if (item) {
|
||||
m_composeView->setTo(item->sender());
|
||||
m_composeView->setSubject("Re: " + item->subject());
|
||||
@@ -268,29 +291,32 @@ void MainMainWindow::onReaderReplyRequested(const MailItem *item) {
|
||||
switchToPage(PageCompose);
|
||||
}
|
||||
|
||||
void MainMainWindow::onNewMessage() {
|
||||
void MainMainWindow::onNewMessage()
|
||||
{
|
||||
switchToPage(PageCompose);
|
||||
}
|
||||
|
||||
void MainMainWindow::openMailInIndependentWindow(int mailId) {
|
||||
void MainMainWindow::openMailInIndependentWindow(int mailId)
|
||||
{
|
||||
std::optional<MailItem> item = MailItemDao::findById(mailId);
|
||||
if (!item.has_value()) return;
|
||||
|
||||
QMainWindow *detachedWin = new QMainWindow();
|
||||
detachedWin->setWindowTitle(QString("Mail - %1").arg(item->subject()));
|
||||
|
||||
|
||||
ReaderView *detachedReader = new ReaderView();
|
||||
detachedReader->setMailItem(&item.value());
|
||||
|
||||
|
||||
detachedWin->setCentralWidget(detachedReader);
|
||||
detachedWin->resize(800, 600);
|
||||
detachedWin->setAttribute(Qt::WA_DeleteOnClose);
|
||||
detachedWin->show();
|
||||
|
||||
|
||||
statusBar()->showMessage("Opened mail in independent window", 3000);
|
||||
}
|
||||
|
||||
void MainMainWindow::createToolBar() {
|
||||
void MainMainWindow::createToolBar()
|
||||
{
|
||||
m_toolBar = addToolBar("Main Toolbar");
|
||||
m_toolBar->setMovable(false);
|
||||
|
||||
@@ -317,4 +343,48 @@ void MainMainWindow::createToolBar() {
|
||||
connect(deleteAction, &QAction::triggered, [this]() {
|
||||
statusBar()->showMessage("Delete would be implemented here", 3000);
|
||||
});
|
||||
}
|
||||
|
||||
void MainMainWindow::onProgressChanged(int percent)
|
||||
{
|
||||
if (percent < 0 || percent > 100) {
|
||||
m_progressBar->setVisible(false);
|
||||
return;
|
||||
}
|
||||
m_progressBar->setValue(percent);
|
||||
m_progressBar->setVisible(true);
|
||||
}
|
||||
|
||||
void MainMainWindow::onStatusMessage(const QString &msg)
|
||||
{
|
||||
// Show temporary message in status bar (timeout 5000 ms)
|
||||
statusBar()->showMessage(msg, 5000);
|
||||
}
|
||||
|
||||
void MainMainWindow::onAddAccountRequested()
|
||||
{
|
||||
AccountSetupDialog *dialog = new AccountSetupDialog(m_accountService, this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setWindowFlag(Qt::Window, true); // make it an independent window
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void MainMainWindow::onAccountEditRequested(int accountId)
|
||||
{
|
||||
QMessageBox::information(this, "Debug", QString("Edit account slot called for ID %1").arg(accountId));
|
||||
Account* account = m_accountService->findAccountById(accountId);
|
||||
if (!account) {
|
||||
statusBar()->showMessage(QString("Error: account %1 not found").arg(accountId), 3000);
|
||||
return;
|
||||
}
|
||||
AccountSetupDialog dlg(m_accountService, this);
|
||||
dlg.loadAccountForEditing(*account);
|
||||
dlg.exec();
|
||||
delete account;
|
||||
}
|
||||
|
||||
void MainMainWindow::onAccountDeleteRequested(int accountId)
|
||||
{
|
||||
Q_UNUSED(accountId);
|
||||
statusBar()->showMessage(QString("Delete account %1 requested").arg(accountId), 3000);
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <QSplitter>
|
||||
#include <QTreeView>
|
||||
#include <QStatusBar>
|
||||
#include <QProgressBar>
|
||||
#include <QToolBar>
|
||||
#include <QAction>
|
||||
|
||||
@@ -35,6 +36,11 @@ private slots:
|
||||
void onReaderReplyRequested(const MailItem *item);
|
||||
void onNewMessage();
|
||||
void openMailInIndependentWindow(int mailId);
|
||||
void onAddAccountRequested();
|
||||
void onAccountEditRequested(int accountId);
|
||||
void onAccountDeleteRequested(int accountId);
|
||||
void onProgressChanged(int percent);
|
||||
void onStatusMessage(const QString &msg);
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
@@ -78,4 +84,6 @@ private:
|
||||
QToolBar *m_toolBar;
|
||||
int m_currentFolderId;
|
||||
int m_currentMailId;
|
||||
|
||||
QProgressBar *m_progressBar;
|
||||
};
|
||||
@@ -43,7 +43,7 @@ int FolderTreeItem::row() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
FolderTreeItem *FolderTreeItem::parentItem()
|
||||
FolderTreeItem *FolderTreeItem::parentItem() const
|
||||
{
|
||||
return m_parentItem;
|
||||
}
|
||||
@@ -53,9 +53,14 @@ FolderTreeItem *FolderTreeItem::parentItem()
|
||||
FolderListModel::FolderListModel(AccountService *accountService, QObject *parent)
|
||||
: QAbstractItemModel(parent),
|
||||
m_accountService(accountService),
|
||||
m_rootItem(new FolderTreeItem(FolderTreeItem::AccountNode, "Root"))
|
||||
m_rootItem(new FolderTreeItem(FolderTreeItem::AccountNode, QStringLiteral("Root")))
|
||||
{
|
||||
refresh();
|
||||
// Connect to account list changes
|
||||
if (m_accountService) {
|
||||
connect(m_accountService, &AccountService::accountListChanged,
|
||||
this, &FolderListModel::onAccountListChanged);
|
||||
}
|
||||
}
|
||||
|
||||
FolderListModel::~FolderListModel()
|
||||
@@ -151,27 +156,38 @@ QVariant FolderListModel::data(const QModelIndex &index, int role) const
|
||||
if (role == UnreadCountRole) {
|
||||
if (item->type() == FolderTreeItem::FolderNode) {
|
||||
Folder folder = item->data().value<Folder>();
|
||||
return folder.unreadCount();
|
||||
// TODO: compute unread count
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool FolderListModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_UNUSED(value);
|
||||
Q_UNUSED(role);
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FolderListModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> FolderListModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[Qt::DisplayRole] = "display";
|
||||
roles[NameRole] = "name";
|
||||
roles[ItemTypeRole] = "itemType";
|
||||
roles[AccountIdRole] = "accountId";
|
||||
roles[FolderIdRole] = "folderId";
|
||||
roles[NameRole] = "name";
|
||||
roles[UnreadCountRole] = "unreadCount";
|
||||
return roles;
|
||||
}
|
||||
@@ -182,44 +198,31 @@ void FolderListModel::refresh()
|
||||
clearModel();
|
||||
setupModelData();
|
||||
endResetModel();
|
||||
qDebug() << "FolderListModel refreshed";
|
||||
}
|
||||
|
||||
void FolderListModel::onAccountListChanged()
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
void FolderListModel::setupModelData()
|
||||
{
|
||||
if (!m_accountService)
|
||||
return;
|
||||
|
||||
QVector<Account> accounts = m_accountService->getAllAccounts();
|
||||
|
||||
// If no accounts exist yet, add one sample account with default folders
|
||||
if (accounts.isEmpty()) {
|
||||
Account sample(1, "javi@example.com", "Javi's Email",
|
||||
AccountType::IMAP);
|
||||
accounts.append(sample);
|
||||
}
|
||||
|
||||
for (const Account &acc : accounts) {
|
||||
QVariant accVariant;
|
||||
accVariant.setValue(acc);
|
||||
FolderTreeItem *accountItem = new FolderTreeItem(FolderTreeItem::AccountNode, accVariant, m_rootItem);
|
||||
QVariant accData;
|
||||
accData.setValue(acc);
|
||||
FolderTreeItem *accountItem = new FolderTreeItem(FolderTreeItem::AccountNode, accData, m_rootItem);
|
||||
m_rootItem->appendChild(accountItem);
|
||||
|
||||
// Get folders for this account
|
||||
// Load folders for this account
|
||||
QVector<Folder> folders = FolderDao::findByAccountId(acc.id());
|
||||
|
||||
// If no folders exist yet, create default ones
|
||||
if (folders.isEmpty()) {
|
||||
QStringList defaultFolders = {"Inbox", "Sent", "Drafts", "Trash", "Spam"};
|
||||
for (const QString &name : defaultFolders) {
|
||||
Folder f;
|
||||
f.setName(name);
|
||||
f.setAccountId(acc.id());
|
||||
folders.append(f);
|
||||
}
|
||||
}
|
||||
|
||||
for (const Folder &folder : folders) {
|
||||
QVariant folderVariant;
|
||||
folderVariant.setValue(folder);
|
||||
FolderTreeItem *folderItem = new FolderTreeItem(FolderTreeItem::FolderNode, folderVariant, accountItem);
|
||||
QVariant folderData;
|
||||
folderData.setValue(folder);
|
||||
FolderTreeItem *folderItem = new FolderTreeItem(FolderTreeItem::FolderNode, folderData, accountItem);
|
||||
accountItem->appendChild(folderItem);
|
||||
}
|
||||
}
|
||||
@@ -228,6 +231,4 @@ void FolderListModel::setupModelData()
|
||||
void FolderListModel::clearModel()
|
||||
{
|
||||
m_rootItem->clearChildren();
|
||||
}
|
||||
|
||||
#include "FolderListModel.moc"
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
FolderTreeItem *child(int row);
|
||||
int childCount() const;
|
||||
int row() const;
|
||||
FolderTreeItem *parentItem();
|
||||
FolderTreeItem *parentItem() const;
|
||||
Type type() const { return m_type; }
|
||||
QVariant data() const { return m_data; }
|
||||
|
||||
@@ -54,17 +54,21 @@ public:
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
|
||||
private slots:
|
||||
void onAccountListChanged();
|
||||
|
||||
private:
|
||||
void setupModelData();
|
||||
void clearModel();
|
||||
|
||||
FolderTreeItem *m_rootItem;
|
||||
AccountService *m_accountService;
|
||||
FolderTreeItem *m_rootItem;
|
||||
};
|
||||
|
||||
#endif // FOLDERLISTMODEL_H
|
||||
+156
-76
@@ -2,6 +2,18 @@
|
||||
#include "services/accountservice.h"
|
||||
#include "core/models/account.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QFont>
|
||||
#include <QButtonGroup>
|
||||
#include <QComboBox>
|
||||
#include <QCheckBox>
|
||||
|
||||
SettingsView::SettingsView(QWidget *parent) : QWidget(parent) {
|
||||
setupUI();
|
||||
}
|
||||
@@ -19,124 +31,128 @@ void SettingsView::setupUI() {
|
||||
}
|
||||
|
||||
QWidget* SettingsView::createAccountsTab() {
|
||||
QWidget *w = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(w);
|
||||
QWidget *widget = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(widget);
|
||||
layout->setContentsMargins(20, 20, 20, 20);
|
||||
layout->setSpacing(15);
|
||||
|
||||
QLabel *sectionTitle = new QLabel("Email Accounts");
|
||||
QFont titleFont = sectionTitle->font();
|
||||
QLabel *title = new QLabel("Email Accounts");
|
||||
QFont titleFont = title->font();
|
||||
titleFont.setPointSize(16);
|
||||
titleFont.setBold(true);
|
||||
sectionTitle->setFont(titleFont);
|
||||
layout->addWidget(sectionTitle);
|
||||
title->setFont(titleFont);
|
||||
layout->addWidget(title);
|
||||
|
||||
m_accountList = new QListWidget();
|
||||
m_accountList->setAlternatingRowColors(true);
|
||||
m_accountList->setFrameShape(QFrame::NoFrame);
|
||||
m_accountList->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
m_accountList->setStyleSheet(
|
||||
"QListWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }"
|
||||
"QListWidget::item { padding: 12px; border-bottom: 1px solid #f0f0f0; }"
|
||||
"QListWidget::item:selected { background: #e3f2fd; }"
|
||||
);
|
||||
layout->addWidget(m_accountList, 1);
|
||||
layout->addWidget(m_accountList);
|
||||
|
||||
QPushButton *addBtn = new QPushButton("+ Add Email Account");
|
||||
addBtn->setStyleSheet(
|
||||
"QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; padding: 10px 20px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #1565C0; }"
|
||||
QHBoxLayout *buttonLayout = new QHBoxLayout();
|
||||
m_addBtn = new QPushButton("Add Account");
|
||||
m_addBtn->setStyleSheet(
|
||||
"QPushButton { background: #FFEB3B; color: white; border: none; border-radius: 4px; "
|
||||
"padding: 8px 16px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #FDD835; }"
|
||||
);
|
||||
connect(addBtn, &QPushButton::clicked, this, &SettingsView::accountAddRequested);
|
||||
layout->addWidget(addBtn);
|
||||
buttonLayout->addWidget(m_addBtn);
|
||||
m_editBtn = new QPushButton("Edit");
|
||||
m_editBtn->setEnabled(false);
|
||||
m_editBtn->setStyleSheet(
|
||||
"QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; "
|
||||
"padding: 8px 16px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #FB8C00; }"
|
||||
);
|
||||
m_deleteBtn = new QPushButton("Delete");
|
||||
m_deleteBtn->setEnabled(false);
|
||||
m_deleteBtn->setStyleSheet(
|
||||
"QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; "
|
||||
"padding: 8px 16px; font-weight: bold; }"
|
||||
"QPushButton:hover { background: #E53935; }"
|
||||
);
|
||||
buttonLayout->addWidget(m_editBtn);
|
||||
buttonLayout->addWidget(m_deleteBtn);
|
||||
buttonLayout->addStretch();
|
||||
layout->addLayout(buttonLayout);
|
||||
|
||||
return w;
|
||||
connect(m_accountList, &QListWidget::itemSelectionChanged, this, &SettingsView::onAccountSelectionChanged);
|
||||
connect(m_editBtn, &QPushButton::clicked, this, &SettingsView::onEditClicked);
|
||||
connect(m_deleteBtn, &QPushButton::clicked, this, &SettingsView::onDeleteClicked);
|
||||
connect(m_addBtn, &QPushButton::clicked, this, &SettingsView::accountAddRequested);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
QWidget* SettingsView::createGeneralTab() {
|
||||
QWidget *w = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(w);
|
||||
QWidget *widget = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(widget);
|
||||
layout->setContentsMargins(20, 20, 20, 20);
|
||||
layout->setSpacing(15);
|
||||
|
||||
QLabel *sectionTitle = new QLabel("General Settings");
|
||||
QFont titleFont = sectionTitle->font();
|
||||
QLabel *title = new QLabel("General Settings");
|
||||
QFont titleFont = title->font();
|
||||
titleFont.setPointSize(16);
|
||||
titleFont.setBold(true);
|
||||
sectionTitle->setFont(titleFont);
|
||||
layout->addWidget(sectionTitle);
|
||||
title->setFont(titleFont);
|
||||
layout->addWidget(title);
|
||||
|
||||
QCheckBox *startOnLogin = new QCheckBox("Start application on login");
|
||||
startOnLogin->setChecked(true);
|
||||
connect(startOnLogin, &QCheckBox::toggled, [this](bool checked) {
|
||||
emit settingChanged("start_on_login", checked);
|
||||
connect(startOnLogin, &QCheckBox::toggled, this, [this](bool checked) {
|
||||
emit settingChanged("start_on_login", QVariant(checked));
|
||||
});
|
||||
layout->addWidget(startOnLogin);
|
||||
|
||||
QCheckBox *enableNotifications = new QCheckBox("Enable notifications");
|
||||
enableNotifications->setChecked(true);
|
||||
connect(enableNotifications, &QCheckBox::toggled, [this](bool checked) {
|
||||
emit settingChanged("notifications_enabled", checked);
|
||||
connect(enableNotifications, &QCheckBox::toggled, this, [this](bool checked) {
|
||||
emit settingChanged("enable_notifications", QVariant(checked));
|
||||
});
|
||||
layout->addWidget(enableNotifications);
|
||||
|
||||
QCheckBox *minimizeToTray = new QCheckBox("Minimize to tray on close");
|
||||
minimizeToTray->setChecked(true);
|
||||
connect(minimizeToTray, &QCheckBox::toggled, [this](bool checked) {
|
||||
emit settingChanged("minimize_to_tray", checked);
|
||||
connect(minimizeToTray, &QCheckBox::toggled, this, [this](bool checked) {
|
||||
emit settingChanged("minimize_to_tray", QVariant(checked));
|
||||
});
|
||||
layout->addWidget(minimizeToTray);
|
||||
|
||||
layout->addSpacing(10);
|
||||
|
||||
QHBoxLayout *syncLayout = new QHBoxLayout();
|
||||
QLabel *syncLabel = new QLabel("Sync Interval:");
|
||||
syncLabel->setStyleSheet("font-weight: bold; color: #555;");
|
||||
m_syncIntervalCombo = new QComboBox();
|
||||
m_syncIntervalCombo->addItem("15 minutes", 15);
|
||||
m_syncIntervalCombo->addItem("30 minutes", 30);
|
||||
m_syncIntervalCombo->addItem("60 minutes", 60);
|
||||
m_syncIntervalCombo->addItem("120 minutes", 120);
|
||||
m_syncIntervalCombo->setCurrentIndex(1);
|
||||
connect(m_syncIntervalCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int idx) {
|
||||
emit settingChanged("sync_interval", m_syncIntervalCombo->itemData(idx));
|
||||
});
|
||||
syncLayout->addWidget(syncLabel);
|
||||
syncLayout->addWidget(m_syncIntervalCombo);
|
||||
syncLayout->addStretch();
|
||||
layout->addLayout(syncLayout);
|
||||
|
||||
layout->addStretch();
|
||||
return w;
|
||||
return widget;
|
||||
}
|
||||
|
||||
QWidget* SettingsView::createAppearanceTab() {
|
||||
QWidget *w = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(w);
|
||||
QWidget *widget = new QWidget();
|
||||
QVBoxLayout *layout = new QVBoxLayout(widget);
|
||||
layout->setContentsMargins(20, 20, 20, 20);
|
||||
layout->setSpacing(15);
|
||||
|
||||
QLabel *sectionTitle = new QLabel("Appearance");
|
||||
QFont titleFont = sectionTitle->font();
|
||||
QLabel *title = new QLabel("Appearance");
|
||||
QFont titleFont = title->font();
|
||||
titleFont.setPointSize(16);
|
||||
titleFont.setBold(true);
|
||||
sectionTitle->setFont(titleFont);
|
||||
layout->addWidget(sectionTitle);
|
||||
title->setFont(titleFont);
|
||||
layout->addWidget(title);
|
||||
|
||||
QLabel *themeLabel = new QLabel("Theme:");
|
||||
themeLabel->setStyleSheet("font-weight: bold; color: #555;");
|
||||
QLabel *themeLabel = new QLabel("Theme");
|
||||
QFont labelFont = themeLabel->font();
|
||||
labelFont.setBold(true);
|
||||
themeLabel->setFont(labelFont);
|
||||
layout->addWidget(themeLabel);
|
||||
|
||||
m_themeGroup = new QButtonGroup(this);
|
||||
QRadioButton *lightRadio = new QRadioButton("Light");
|
||||
QRadioButton *darkRadio = new QRadioButton("Dark");
|
||||
QRadioButton *systemRadio = new QRadioButton("System");
|
||||
lightRadio->setChecked(true);
|
||||
|
||||
m_themeGroup->addButton(lightRadio, 0);
|
||||
m_themeGroup->addButton(darkRadio, 1);
|
||||
m_themeGroup->addButton(systemRadio, 2);
|
||||
|
||||
connect(m_themeGroup, QOverload<int>::of(&QButtonGroup::idClicked), [this](int id) {
|
||||
QHBoxLayout *themeLayout = new QHBoxLayout();
|
||||
themeLayout->addWidget(lightRadio);
|
||||
themeLayout->addWidget(darkRadio);
|
||||
themeLayout->addWidget(systemRadio);
|
||||
layout->addLayout(themeLayout);
|
||||
|
||||
connect(m_themeGroup, QOverload<int>::of(&QButtonGroup::idClicked), this, [this](int id) {
|
||||
QString theme;
|
||||
switch (id) {
|
||||
case 0: theme = "light"; break;
|
||||
@@ -146,19 +162,83 @@ QWidget* SettingsView::createAppearanceTab() {
|
||||
emit themeChanged(theme);
|
||||
});
|
||||
|
||||
layout->addWidget(lightRadio);
|
||||
layout->addWidget(darkRadio);
|
||||
layout->addWidget(systemRadio);
|
||||
|
||||
layout->addSpacing(15);
|
||||
|
||||
QCheckBox *deepinTheme = new QCheckBox("Use Deepin theme");
|
||||
QCheckBox *deepinTheme = new QCheckBox("Deepin theme (experimental)");
|
||||
deepinTheme->setChecked(true);
|
||||
connect(deepinTheme, &QCheckBox::toggled, [this](bool checked) {
|
||||
emit settingChanged("deepin_theme", checked);
|
||||
connect(deepinTheme, &QCheckBox::toggled, this, [this](bool checked) {
|
||||
emit settingChanged("deepin_theme", QVariant(checked));
|
||||
});
|
||||
layout->addWidget(deepinTheme);
|
||||
|
||||
layout->addStretch();
|
||||
return w;
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
void SettingsView::setAccountService(AccountService *service) {
|
||||
m_accountService = service;
|
||||
if (m_accountService) {
|
||||
loadAccounts();
|
||||
connect(m_accountService, &AccountService::accountListChanged, this, &SettingsView::onAccountListChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsView::onAccountListChanged() {
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
void SettingsView::loadAccounts() {
|
||||
if (!m_accountService) {
|
||||
qDebug() << "AccountService not set";
|
||||
return;
|
||||
}
|
||||
m_accountList->clear();
|
||||
QList<Account> accounts = m_accountService->getAllAccounts();
|
||||
qDebug() << "Loaded" << accounts.size() << "accounts from AccountService";
|
||||
for (const Account &acc : accounts) {
|
||||
QString display = acc.displayName().isEmpty() ? acc.email() : acc.displayName();
|
||||
QString text = QString("%1 (%2)").arg(acc.email(), display);
|
||||
QListWidgetItem *item = new QListWidgetItem(text, m_accountList);
|
||||
item->setData(Qt::UserRole, acc.id());
|
||||
m_accountList->addItem(item);
|
||||
}
|
||||
if (m_accountList->count() > 0) {
|
||||
m_accountList->setCurrentRow(0);
|
||||
}
|
||||
}
|
||||
void SettingsView::onAccountSelectionChanged()
|
||||
{
|
||||
QList<QListWidgetItem*> items = m_accountList->selectedItems();
|
||||
qDebug() << "SettingsView::onAccountSelectionChanged, selected items count:" << items.size();
|
||||
if (items.isEmpty()) {
|
||||
m_selectedAccountId = -1;
|
||||
m_editBtn->setEnabled(false);
|
||||
m_deleteBtn->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
QListWidgetItem *item = items.first();
|
||||
m_selectedAccountId = item->data(Qt::UserRole).toInt();
|
||||
qDebug() << "SettingsView::onAccountSelectionChanged, selected account id:" << m_selectedAccountId;
|
||||
m_editBtn->setEnabled(true);
|
||||
m_deleteBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void SettingsView::onEditClicked() {
|
||||
qDebug() << "SettingsView::onEditClicked called, selected account id:" << m_selectedAccountId;
|
||||
if (m_selectedAccountId != -1 && m_accountService) {
|
||||
emit accountEditRequested(m_selectedAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsView::onDeleteClicked() {
|
||||
if (m_selectedAccountId != -1 && m_accountService) {
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle("Delete Account");
|
||||
Account* account = m_accountService->findAccountById(m_selectedAccountId);
|
||||
QString accountName = account ? account->email() : QString::number(m_selectedAccountId);
|
||||
msgBox.setText(QString("Are you sure you want to delete the account \"%1\"?").arg(accountName));
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::No);
|
||||
if (msgBox.exec() == QMessageBox::Yes) {
|
||||
emit accountDeleteRequested(m_selectedAccountId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-2
@@ -12,6 +12,7 @@
|
||||
#include <QRadioButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QVariant>
|
||||
#include "services/accountservice.h"
|
||||
|
||||
class SettingsView : public QWidget {
|
||||
Q_OBJECT
|
||||
@@ -20,13 +21,22 @@ public:
|
||||
explicit SettingsView(QWidget *parent = nullptr);
|
||||
~SettingsView() override = default;
|
||||
|
||||
void setAccountService(AccountService *service);
|
||||
|
||||
signals:
|
||||
void accountAddRequested();
|
||||
void accountEditRequested(int accountIndex);
|
||||
void accountDeleteRequested(int accountIndex);
|
||||
void accountEditRequested(int accountId);
|
||||
void accountDeleteRequested(int accountId);
|
||||
void themeChanged(const QString &theme);
|
||||
void settingChanged(const QString &key, const QVariant &value);
|
||||
|
||||
private slots:
|
||||
void onAccountListChanged();
|
||||
void loadAccounts();
|
||||
void onEditClicked();
|
||||
void onDeleteClicked();
|
||||
void onAccountSelectionChanged();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
QWidget* createAccountsTab();
|
||||
@@ -37,4 +47,10 @@ private:
|
||||
QListWidget *m_accountList;
|
||||
QComboBox *m_syncIntervalCombo;
|
||||
QButtonGroup *m_themeGroup;
|
||||
QPushButton *m_editBtn;
|
||||
QPushButton *m_addBtn;
|
||||
QPushButton *m_deleteBtn;
|
||||
|
||||
AccountService *m_accountService;
|
||||
int m_selectedAccountId;
|
||||
};
|
||||
Reference in New Issue
Block a user