#include "ui/maillistview.h" #include #include "ui/models/EmailListModel.h" #include "ui/models/EmailTreeModel.h" #include #include #include #include #include #include #include #include #include MailListView::MailListView(QWidget *parent) : QWidget(parent), m_sourceModel(nullptr) { setupUI(); } void MailListView::setupUI() { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); // Search bar QWidget *searchBar = new QWidget(); searchBar->setFixedHeight(44); searchBar->setStyleSheet("background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6;"); QHBoxLayout *searchLayout = new QHBoxLayout(searchBar); searchLayout->setContentsMargins(12, 4, 12, 4); searchLayout->setSpacing(8); m_searchEdit = new QLineEdit(); m_searchEdit->setPlaceholderText("Buscar correos…"); m_searchEdit->setClearButtonEnabled(true); m_searchEdit->setStyleSheet( "QLineEdit { background: white; border: 1px solid #d1d1d6; border-radius: 6px; " "padding: 6px 12px; font-size: 13px; color: #1d1d1f; }" "QLineEdit:focus { border: 2px solid #0071e3; }" ); searchLayout->addWidget(m_searchEdit); connect(m_searchEdit, &QLineEdit::textChanged, this, &MailListView::onSearchTextChanged); layout->addWidget(searchBar); // Filter bar QWidget *filterBar = new QWidget(); filterBar->setFixedHeight(36); filterBar->setStyleSheet("background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6;"); QHBoxLayout *filterLayout = new QHBoxLayout(filterBar); filterLayout->setContentsMargins(12, 2, 12, 2); filterLayout->setSpacing(12); m_unreadOnlyCheck = new QCheckBox("No leídos"); m_unreadOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }"); filterLayout->addWidget(m_unreadOnlyCheck); m_flaggedOnlyCheck = new QCheckBox("Marcados"); m_flaggedOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }"); filterLayout->addWidget(m_flaggedOnlyCheck); m_hasAttachmentsCheck = new QCheckBox("Con adjuntos"); m_hasAttachmentsCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }"); filterLayout->addWidget(m_hasAttachmentsCheck); filterLayout->addStretch(); // Group by combo m_groupByCombo = new QComboBox(); m_groupByCombo->addItem("Sin agrupación"); m_groupByCombo->addItem("Agrupar por fecha"); m_groupByCombo->setFixedWidth(180); m_groupByCombo->setStyleSheet( "QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 4px 8px; " "background: white; min-height: 20px; }" "QComboBox:hover { border-color: #bdbdbd; }" "QComboBox:focus { border-color: #1976D2; }" ); connect(m_groupByCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &MailListView::onGroupByChanged); filterLayout->addWidget(m_groupByCombo); layout->addWidget(filterBar); // Tree View m_treeView = new QTreeView(); m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_treeView->setSelectionMode(QAbstractItemView::SingleSelection); m_treeView->setAlternatingRowColors(true); m_treeView->header()->setStretchLastSection(false); m_treeView->header()->setSectionsClickable(true); m_treeView->setSortingEnabled(false); // We handle sorting in the model m_treeView->setFrameShape(QFrame::NoFrame); m_treeView->setRootIsDecorated(true); m_treeView->setItemsExpandable(true); m_treeView->setUniformRowHeights(true); m_treeView->setStyleSheet( "QTreeView { background-color: #ffffff; alternate-background-color: #f9f9fb; border: none; }" "QTreeView::item { padding: 8px; border-bottom: 1px solid #e8e8ed; }" "QTreeView::item:selected { background-color: #e3f2fd; color: #1a1a2e; }" "QTreeView::branch { background: transparent; }" "QHeaderView::section { background-color: #f5f5f7; padding: 8px; border: none; border-bottom: 1px solid #d1d1d6; font-weight: 600; color: #555; }" ); // Tree model m_treeModel = new EmailTreeModel(this); m_proxyModel = new QSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_treeModel); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_proxyModel->setFilterKeyColumn(-1); // Search all columns m_treeView->setModel(m_proxyModel); // Column widths m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSubject, QHeaderView::Stretch); m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSender, QHeaderView::Stretch); m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColDate, QHeaderView::ResizeToContents); connect(m_treeView, &QTreeView::clicked, this, &MailListView::onRowSelected); connect(m_treeView, &QTreeView::doubleClicked, this, [this](const QModelIndex &index) { if (!index.isValid()) return; QModelIndex proxyIdx = m_proxyModel->mapToSource(index); if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) { int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt(); emit emailOpenRequested(mailId); } }); // Expand all groups by default m_treeView->expandAll(); layout->addWidget(m_treeView); // Connect filter checkboxes connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); } void MailListView::setModel(EmailListModel *model) { m_sourceModel = model; // Connect to source model changes to refresh tree connect(m_sourceModel, &QAbstractItemModel::modelReset, this, &MailListView::refreshTreeModel); connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this, &MailListView::refreshTreeModel); connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, &MailListView::refreshTreeModel); connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &MailListView::refreshTreeModel); // Initial population refreshTreeModel(); } void MailListView::refreshTreeModel() { if (!m_sourceModel) return; const QVector &emails = m_sourceModel->emails(); m_treeModel->setEmails(emails); m_treeView->expandAll(); } void MailListView::onRowSelected(const QModelIndex &index) { if (!index.isValid()) return; QModelIndex proxyIdx = m_proxyModel->mapToSource(index); if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) { int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt(); emit emailSelected(mailId); } } void MailListView::onSearchTextChanged(const QString &text) { m_proxyModel->setFilterFixedString(text); } void MailListView::onFilterChanged() { if (!m_sourceModel) return; m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked()); m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked()); m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked()); // refreshTreeModel will be called automatically via modelReset signal } void MailListView::onGroupByChanged(int index) { bool enabled = (index == 1); // 0 = Sin agrupación, 1 = Agrupar por fecha m_treeModel->setGroupMode(enabled ? EmailTreeModel::GroupByDate : EmailTreeModel::NoGrouping); m_treeView->expandAll(); }