270 lines
9.2 KiB
C++
270 lines
9.2 KiB
C++
#include "mainmainwindow.h"
|
|||
|
|
#include "core/models/account.h"
|
||
|
|
#include "core/mailitem.h"
|
||
|
|
#include "db/dao/mailitemdao.h"
|
||
|
|
#include <QVBoxLayout>
|
||
|
|
#include <QHBoxLayout>
|
||
|
|
#include <QFrame>
|
||
|
|
#include <QDateTime>
|
||
|
|
#include <QLabel>
|
||
|
|
|
||
|
|
MainMainWindow::MainMainWindow(QWidget *parent)
|
||
|
|
: QMainWindow(parent), m_currentFolderId(-1)
|
||
|
|
{
|
||
|
|
setupUI();
|
||
|
|
connectModels();
|
||
|
|
setWindowTitle("Wino Mail DTK");
|
||
|
|
resize(1280, 820);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::setupUI() {
|
||
|
|
// === Global stylesheet ===
|
||
|
|
this->setStyleSheet(
|
||
|
|
"QMainWindow { background-color: #f5f5f7; }"
|
||
|
|
"QSplitter::handle { background-color: #d1d1d6; width: 1px; }"
|
||
|
|
"QTreeView { background-color: #ffffff; border: none; font-family: 'Segoe UI', Helvetica; font-size: 13px; }"
|
||
|
|
"QToolBar { background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6; spacing: 10px; }"
|
||
|
|
);
|
||
|
|
|
||
|
|
createToolBar();
|
||
|
|
|
||
|
|
// === Central widget ===
|
||
|
|
QWidget *central = new QWidget();
|
||
|
|
QHBoxLayout *centralLayout = new QHBoxLayout(central);
|
||
|
|
centralLayout->setContentsMargins(0, 0, 0, 0);
|
||
|
|
centralLayout->setSpacing(0);
|
||
|
|
|
||
|
|
// === Sidebar ===
|
||
|
|
setupSidebar();
|
||
|
|
centralLayout->addWidget(m_sidebar);
|
||
|
|
|
||
|
|
// === Separator line ===
|
||
|
|
QFrame *separator = new QFrame();
|
||
|
|
separator->setFrameShape(QFrame::VLine);
|
||
|
|
separator->setStyleSheet("color: #d1d1d6;");
|
||
|
|
centralLayout->addWidget(separator);
|
||
|
|
|
||
|
|
// === Stacked pages ===
|
||
|
|
m_stack = new QStackedWidget();
|
||
|
|
m_stack->setStyleSheet("background-color: #f5f5f7;");
|
||
|
|
|
||
|
|
// Page 0: Mail (folder tree + mail list + reader)
|
||
|
|
setupMailPage();
|
||
|
|
m_stack->addWidget(m_mailPage);
|
||
|
|
|
||
|
|
// 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) {
|
||
|
|
QString msg;
|
||
|
|
if (scheduleTime.isValid()) {
|
||
|
|
msg = QString("Message scheduled for: %1").arg(scheduleTime.toString("dd/MM/yyyy hh:mm AP"));
|
||
|
|
} else {
|
||
|
|
msg = "Message sent (simulated)";
|
||
|
|
}
|
||
|
|
if (!cc.isEmpty()) msg += QString(" | Cc: %1").arg(cc);
|
||
|
|
if (!bcc.isEmpty()) msg += QString(" | Bcc: %1").arg(bcc);
|
||
|
|
statusBar()->showMessage(msg, 5000);
|
||
|
|
switchToPage(PageMail);
|
||
|
|
});
|
||
|
|
connect(m_composeView, &ComposeView::discardRequested, [this]() {
|
||
|
|
switchToPage(PageMail);
|
||
|
|
});
|
||
|
|
connect(m_composeView, &ComposeView::detachRequested, [this](QWidget *composeView) {
|
||
|
|
// Detach compose view to a standalone window
|
||
|
|
QStackedWidget *stack = qobject_cast<QStackedWidget*>(composeView->parentWidget());
|
||
|
|
if (stack) {
|
||
|
|
stack->removeWidget(composeView);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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);
|
||
|
|
|
||
|
|
// Page 2: Settings
|
||
|
|
m_settingsView = new SettingsView();
|
||
|
|
connect(m_settingsView, &SettingsView::accountAddRequested, [this]() {
|
||
|
|
statusBar()->showMessage("Account setup dialog would open here", 3000);
|
||
|
|
});
|
||
|
|
connect(m_settingsView, &SettingsView::themeChanged, [this](const QString &theme) {
|
||
|
|
statusBar()->showMessage(QString("Theme changed to: %1 (restart may be required)").arg(theme), 3000);
|
||
|
|
});
|
||
|
|
m_stack->addWidget(m_settingsView);
|
||
|
|
|
||
|
|
// Page 3: Contacts
|
||
|
|
m_contactsView = new ContactsView();
|
||
|
|
m_stack->addWidget(m_contactsView);
|
||
|
|
|
||
|
|
// Page 4: Calendar
|
||
|
|
m_calendarView = new CalendarView();
|
||
|
|
m_stack->addWidget(m_calendarView);
|
||
|
|
|
||
|
|
centralLayout->addWidget(m_stack, 1);
|
||
|
|
setCentralWidget(central);
|
||
|
|
|
||
|
|
// Show mail page by default
|
||
|
|
switchToPage(PageMail);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::setupSidebar() {
|
||
|
|
m_sidebar = new QListWidget();
|
||
|
|
m_sidebar->setFixedWidth(64);
|
||
|
|
m_sidebar->setIconSize(QSize(24, 24));
|
||
|
|
m_sidebar->setSpacing(4);
|
||
|
|
m_sidebar->setFrameShape(QFrame::NoFrame);
|
||
|
|
m_sidebar->setStyleSheet(
|
||
|
|
"QListWidget { background-color: #2c2c2e; border: none; padding: 8px 0; }"
|
||
|
|
"QListWidget::item { color: #8e8e93; padding: 12px 0; text-align: center; font-size: 10px; border: none; border-radius: 8px; margin: 2px 8px; }"
|
||
|
|
"QListWidget::item:selected { background-color: #3a3a3c; color: #ffffff; }"
|
||
|
|
"QListWidget::item:hover { background-color: #3a3a3c; color: #ffffff; }"
|
||
|
|
);
|
||
|
|
|
||
|
|
m_sidebar->addItem("📧\nMail");
|
||
|
|
m_sidebar->addItem("✏️\nCompose");
|
||
|
|
m_sidebar->addItem("⚙️\nSettings");
|
||
|
|
m_sidebar->addItem("👥\nContacts");
|
||
|
|
m_sidebar->addItem("📅\nCalendar");
|
||
|
|
|
||
|
|
m_sidebar->setCurrentRow(0);
|
||
|
|
connect(m_sidebar, &QListWidget::currentRowChanged, this, &MainMainWindow::onNavChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::setupMailPage() {
|
||
|
|
m_mailPage = new QWidget();
|
||
|
|
QHBoxLayout *mailLayout = new QHBoxLayout(m_mailPage);
|
||
|
|
mailLayout->setContentsMargins(0, 0, 0, 0);
|
||
|
|
mailLayout->setSpacing(0);
|
||
|
|
|
||
|
|
m_folderSplitter = new QSplitter(Qt::Horizontal);
|
||
|
|
m_folderSplitter->setHandleWidth(1);
|
||
|
|
|
||
|
|
// Folder tree
|
||
|
|
m_folderTree = new QTreeView();
|
||
|
|
m_folderTree->setHeaderHidden(true);
|
||
|
|
m_folderTree->setIndentation(20);
|
||
|
|
m_folderTree->setMinimumWidth(220);
|
||
|
|
m_folderTree->setMaximumWidth(350);
|
||
|
|
m_folderTree->setFrameShape(QFrame::NoFrame);
|
||
|
|
m_folderTree->setExpandsOnDoubleClick(true);
|
||
|
|
m_folderSplitter->addWidget(m_folderTree);
|
||
|
|
|
||
|
|
// Mail list (QTableView with sorting)
|
||
|
|
m_mailListView = new MailListView();
|
||
|
|
connect(m_mailListView, &MailListView::emailSelected, this, &MainMainWindow::onEmailSelected);
|
||
|
|
connect(m_mailListView, &MailListView::composeRequested, this, &MainMainWindow::onComposeRequested);
|
||
|
|
m_folderSplitter->addWidget(m_mailListView);
|
||
|
|
|
||
|
|
// Reader
|
||
|
|
m_emailViewer = new ReaderView();
|
||
|
|
m_emailViewer->setMinimumWidth(350);
|
||
|
|
connect(m_emailViewer, &ReaderView::replyRequested, this, &MainMainWindow::onReaderReplyRequested);
|
||
|
|
m_folderSplitter->addWidget(m_emailViewer);
|
||
|
|
|
||
|
|
// Default sizes: folder 240, list 380, reader flex
|
||
|
|
m_folderSplitter->setSizes({240, 380, 600});
|
||
|
|
|
||
|
|
mailLayout->addWidget(m_folderSplitter);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::connectModels() {
|
||
|
|
m_accountService = new AccountService(this);
|
||
|
|
m_mailService = new MailService(this);
|
||
|
|
|
||
|
|
m_folderModel = new FolderListModel(m_accountService, this);
|
||
|
|
m_emailModel = new EmailListModel(this);
|
||
|
|
|
||
|
|
m_folderTree->setModel(m_folderModel);
|
||
|
|
m_mailListView->setModel(m_emailModel);
|
||
|
|
|
||
|
|
m_folderTree->expandAll();
|
||
|
|
|
||
|
|
connect(m_folderTree, &QTreeView::clicked, this, &MainMainWindow::onFolderSelected);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onNavChanged(int index) {
|
||
|
|
switchToPage(static_cast<Page>(index));
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::switchToPage(int pageIndex) {
|
||
|
|
m_stack->setCurrentIndex(pageIndex);
|
||
|
|
m_sidebar->blockSignals(true);
|
||
|
|
m_sidebar->setCurrentRow(pageIndex);
|
||
|
|
m_sidebar->blockSignals(false);
|
||
|
|
|
||
|
|
// Show/hide toolbar actions per page
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onFolderSelected(const QModelIndex &index) {
|
||
|
|
if (!index.isValid()) return;
|
||
|
|
|
||
|
|
int itemType = index.data(FolderListModel::ItemTypeRole).toInt();
|
||
|
|
if (itemType == FolderTreeItem::FolderNode) {
|
||
|
|
m_currentFolderId = index.data(FolderListModel::FolderIdRole).toInt();
|
||
|
|
m_emailModel->setFolderId(m_currentFolderId);
|
||
|
|
m_emailModel->refresh();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onEmailSelected(int mailId) {
|
||
|
|
std::optional<MailItem> item = MailItemDao::findById(mailId);
|
||
|
|
if (!item.has_value()) {
|
||
|
|
m_emailViewer->setMailItem(nullptr);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
MailItem &mail = item.value();
|
||
|
|
if (!mail.isRead()) {
|
||
|
|
mail.setRead(true);
|
||
|
|
MailItemDao::update(mail);
|
||
|
|
}
|
||
|
|
m_emailViewer->setMailItem(&mail);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onComposeRequested() {
|
||
|
|
switchToPage(PageCompose);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onReaderReplyRequested(const MailItem *item) {
|
||
|
|
if (item) {
|
||
|
|
m_composeView->setTo(item->sender());
|
||
|
|
m_composeView->setSubject("Re: " + item->subject());
|
||
|
|
}
|
||
|
|
switchToPage(PageCompose);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::onNewMessage() {
|
||
|
|
switchToPage(PageCompose);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MainMainWindow::createToolBar() {
|
||
|
|
m_toolBar = addToolBar("Main Toolbar");
|
||
|
|
m_toolBar->setMovable(false);
|
||
|
|
|
||
|
|
QAction *newMsgAction = m_toolBar->addAction("✉ New Message");
|
||
|
|
m_toolBar->addSeparator();
|
||
|
|
QAction *syncAction = m_toolBar->addAction("⟳ Sync/Refresh");
|
||
|
|
QAction *deleteAction = m_toolBar->addAction("🗑 Delete");
|
||
|
|
|
||
|
|
connect(newMsgAction, &QAction::triggered, this, &MainMainWindow::onNewMessage);
|
||
|
|
connect(syncAction, &QAction::triggered, [this]() {
|
||
|
|
if (m_currentFolderId >= 0) {
|
||
|
|
m_emailModel->refresh();
|
||
|
|
statusBar()->showMessage("Refreshed", 2000);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
connect(deleteAction, &QAction::triggered, [this]() {
|
||
|
|
statusBar()->showMessage("Delete would be implemented here", 3000);
|
||
|
|
});
|
||
|
|
}
|